// The NSpring Framework for .NET
// Copyright (c) 2003, Jeffrey Varszegi
// All rights reserved.
// 
// Redistribution and use in source and binary forms, with or without modification, 
// are permitted provided that the following conditions are met:
// 
// - Redistributions of source code must retain the above copyright notice, this 
// list of conditions and the following disclaimer.
// 
// - Redistributions in binary form must reproduce the above copyright notice, this 
// list of conditions and the following disclaimer in the documentation and/or other 
// materials provided with the distribution.
// 
// - Neither the name of the NSpring project nor the names of its contributors may 
// be used to endorse or promote products derived from this software without 
// specific prior written permission from the copyright owner.
// 
// - No product derived from this software may be called "NSpring", nor may 
// "NSpring" appear in the name of such a product, without specific prior written 
// permission from the copyright owner.
// 
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND 
// ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED 
// WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. 
// IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, 
// INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, 
// BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 
// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF 
// LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE 
// OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED 
// OF THE POSSIBILITY OF SUCH DAMAGE. 

namespace NSpring.Logging
{

	using System;
	using System.Collections;
	using System.Data;
	using System.IO;
	using System.Net;
	using System.Text;
	using System.Threading;

	using Common;
	using Filters;
	using EventFormatters;
	using Exceptions;
	using Loggers;
	using System.Diagnostics;

	/// <summary>
	/// <para>
	/// The base class for all representations of physical output in the logging framework.
	/// Many predefined Logger types are available in the NSpring.Logging.Loggers namespace.
	/// Each built-in type provides a different sort of output; for instance, the ConsoleLogger class
	/// can be used to send output to the command line, the EmailLogger can be used to send log events
	/// as email, etc.
	/// </para>
	/// <para>
	/// Loggers in the framework have the following basic abilities, available also to user-defined
	/// Logger subclasses:
	/// </para>
	/// <para>
	/// Buffering: This is the ability for application code to dump messages into a Logger and continue
	/// on without waiting for the message to actually be written to output.  Each Logger that exists
	/// in buffering mode maintains a separate thread to consume its buffer, on a scheduled basis and
	/// each time the buffer reaches its maximum capacity.  The IsBufferingEnabled and BufferSize
	/// properties can be used to set this behavior.<br>
	/// The Logger class uses a two-stage buffer; the contents of the buffer are rotated into a seconday buffer
	/// and then written to output, while the primary buffer is released to be filled simultaneously by
	/// application code.  This results in greater throughput when the buffer is at or past its maximum capacity.
	/// In addition, the buffer itself is able to temporarily grow past its set maximum size, to avoid a logjam situation,
	/// where application code must wait while buffered content is written to output.
	/// </para>
	/// <para>
	/// Thread safety: All methods on all classes are thread-safe.
	/// </para>
	/// <para>
	/// Extensible filtering: This is the ability for a logger to reject certain messages based
	/// on flexible criteria.  Each Filter subclass in the NSpring.Logging.Filters 
	/// namespace defines a different sort of selection criterion, and these criteria can be 
	/// combined using the CompositeFilter class.  Each Logger can also perform its own simple 
	/// filtering based on level, by setting its Level property.
	/// </para>
	/// <para>
	/// Extensible formatting: The NSpring.Logging.EventFormatter class defines semantics for adding custom
	/// formatting logic if necessary.  Two predefined formatting classes are provided in the NSpring.Logging.EventFormatters
	/// namespace; SimpleEventFormatter provides basic flat-log formatting, while XMLEventFormatter provides
	/// configurable, performance-optimized XML formatting.  If these are not sufficient, user-defined formatters
	/// can be set on any Logger instance using the EventFormatter property, replacing the logger's default formatting logic.
	/// </para>
	/// <para>
	/// Extensible logging: The only method required to be implemented by Logger subclasses is the Write(Event) method.  For bulk
	/// speed purposes, subclass authors may choose to extend the Write(Event[]) method as well, but it's not
	/// required.  Startup and shutdown hooks are provided, if necessary, through the virtual methods
	/// AcquireResources() and ReleaseResources(), called whenever the logger is opened and closed.  So, for instance,
	/// to implement a custom database-logging utility, a programmer would be able to get away with
	/// implementing just three methods.
	/// </para>
	/// <para>
	/// Log nesting/composition: This isc  the ability to add child loggers to an existing Logger instance.  When
	/// a parent logs an event, it passes it on to its children as well.  This can be useful in many ways.
	/// First, it allows the same message to be written to many output devices with one call from application code.
	/// It also allows the unification of many loggers under a single name (more about that in a minute), and allows the 
	/// a single buffer (in the parent logger) to be used by all children, if that's desired.  Any Logger
	/// instance can receive children by use of the AddChild() method, but the CompositeLogger class is
	/// provided for these latter situations.
	/// </para>
	/// <para>
	/// Exception handling: Each Logger instance uses an NSpring.Logging.Exceptions.ExceptionHandler to
	/// handle errors that may occur during logging (for instance, due to a resource outage such as a database
	/// being unavailable).  By default, this is an instance of NSpring.Logging.Exceptions.SilentExceptionHandler,
	/// which does nothing; however, this may be replaced via the logger's ExceptionHandler property.  Users are free
	/// to construct their own ExceptionHandler implementations, and the Logger class itself implements ExceptionHandler,
	/// allowing one logger to act as a failover logger for another.
	/// </para>
	/// <para>
	/// Naming: Each Logger can be registered with a LoggerRegistry.  A default 
	/// instance of this registry is maintained by the Logger class, accessible through static 
	/// methods.  Using these methods (AddLogger(), etc.) a Logger instance can be provided for 
	/// global access based on an easy-to-remember name.  Separate LoggerRegistry instances
	/// can be maintained by different modules, if necessary.
	/// </para>
	/// <para>
	/// Speed: Writing a message to any logger in buffering mode results in ~.5 microseconds 
	/// (millionths of a second) processing delay in an application thread on a P4-M 2.0 GHz machine.
	/// Your mileage may vary, but this is faster than other logging methods available.
	/// For instance, this package offers a significant performance benefit when compared with the use of 
	/// System.Diagnostics classes Debug and Trace; even when no listeners are defined on Debug or 
	/// Trace, it is many times faster than either one.
	/// </para>
	/// </summary>
	public abstract class Logger : IExceptionHandler
	{
		private static char[] HEX_CHARS = { '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'A', 'B', 'C', 'D', 'E', 'F' };

		private static IExceptionHandler defaultExceptionHandler = new SilentExceptionHandler();

		private IExceptionHandler exceptionHandler = defaultExceptionHandler;

		private const int DEFAULT_BUFFER_SIZE = 100000;
		private const int MINIMUM_BUFFER_SIZE = 5;
		private const int DEFAULT_AUTO_FLUSH_INTERVAL = 15000;
		private const int MINIMUM_AUTO_FLUSH_INTERVAL = 1000;

		internal readonly static LoggerRegistry registry = LoggerRegistry.DefaultInstance;

		private readonly Hashtable registrations = new Hashtable(100);

		protected bool isClosed = true;

		private Level defaultEventLevel = Level.Info;
		private Level level = Level.All;
		private int levelPriority = Level.All.Priority;

		private Logger[] children = new Logger[5];
		private int childCount = 0;
		private readonly object childMonitor = new object();

		private static bool isContextEnabledByDefault = false;
		private bool isContextEnabled = isContextEnabledByDefault;

		private static bool isExceptionAutoFlushEnabledByDefault = false;
		private bool isExceptionAutoFlushEnabled = isExceptionAutoFlushEnabledByDefault;

		private bool isBufferingEnabled = false;
		private int bufferSize = DEFAULT_BUFFER_SIZE;
		private int maximumBufferSize = Int32.MaxValue;
		private int bufferCount = 0;
		//
		private Event[] writeBuffer;
		private int writeBufferCount = 0;
		private readonly object writeBufferMonitor = new object();
		private int autoFlushInterval = DEFAULT_AUTO_FLUSH_INTERVAL;

		// Used in constructing event IDs.  Two sets of randomizing elements are allocated,
		// for use in different synchronization contexts
		private Random random1 = new Random((int)DateTime.Now.Ticks);
		private byte[] byteArray1 = new byte[16];
		private char[] charArray1 = new char[32];
		private Random random2 = new Random((int)(DateTime.Now.Ticks - 371));
		private byte[] byteArray2 = new byte[16];
		private char[] charArray2 = new char[32];

		protected Encoding encoding = Encoding.UTF8;

		private Context[] contextBuffer;
		private Level[] levelBuffer;
		private long[] timestampBuffer;
		private string[] messageBuffer;
		private object[] dataBuffer;
		private string[] categoryBuffer;
		private string[] codeBuffer;
		private string[] idBuffer;

		private Context[] contextWriteBuffer;
		private Level[] levelWriteBuffer;
		private long[] timestampWriteBuffer;
		private string[] messageWriteBuffer;
		private object[] dataWriteBuffer;
		private string[] categoryWriteBuffer;
		private string[] codeWriteBuffer;
		private string[] idWriteBuffer;

		private Context[] tempContextBuffer;
		private Level[] tempLevelBuffer;
		private long[] tempTimestampBuffer;
		private string[] tempMessageBuffer;
		private object[] tempDataBuffer;
		private string[] tempCategoryBuffer;
		private string[] tempCodeBuffer;
		private string[] tempIDBuffer;

		private FlushingThread flushingThread = null;
		private ThreadPriority threadPriority = ThreadPriority.AboveNormal;

		private Filter filter = null;
		protected EventFormatter eventFormatter;
		private bool isIDGenerationEnabled = false;


		/// <summary>
		/// Used in synchronization.  Loggers should synchronize on this object when they must guarantee
		/// thread safety at all costs
		/// </summary>
		protected readonly object monitor = new object();

		/// <summary>
		/// Adds a logger with the specified name to the default registry (LoggerRegistry.DefaultInstance)
		/// </summary>
		/// <param name="name">The name with which to register the logger</param>
		/// <param name="logger">The logger to register</param>
		public static void AddLogger(string name, Logger logger)
		{
			registry.AddLogger(name, logger);
		}
		/// <summary>
		/// Deregisters the logger with the specified name from the default registry (LoggerRegistry.DefaultInstance)
		/// </summary>
		/// <param name="name">The name to deregister</param>
		public static void RemoveLogger(string name)
		{
			registry.RemoveLogger(name);
		}
		/// <summary>
		/// Deregisters the specified logger from the default registry (LoggerRegistry.DefaultInstance)
		/// </summary>
		/// <param name="logger">The logger to deregister</param>
		public static void RemoveLogger(Logger logger)
		{
			registry.RemoveLogger(logger);
		}
		public static void OpenLoggers()
		{
			registry.Open();
		}
		/// <summary>
		/// Closes all loggers registered with the default registry.  This is the 
		/// same as calling LoggerRegistry.DefaultInstance.Close()
		/// </summary>
		public static void CloseLoggers()
		{
			registry.Close();
		}

		/// <summary>
		/// Gets the logger with the specified name from the default registry (LoggerRegistry.DefaultInstance)
		/// </summary>
		/// <param name="name">The name of the required logger</param>
		/// <returns>The logger registered with the specified name</returns>
		public static Logger GetLogger(string name)
		{
			return registry.GetLogger(name);
		}

		/// <summary>
		/// Gets an array containing all the loggers registered with the default registry (LoggerRegistry.DefaultInstance)
		/// </summary>
		public static Logger[] Loggers { get { return registry.Loggers; } }

		/// <summary>
		/// Gets the names of all the loggers registered with the default registry (LoggerRegistry.DefaultInstance)
		/// </summary>
		public static string[] LoggerNames { get { return registry.LoggerNames; } }

		/// <summary>
		/// Indicates whether the registry (LoggerRegistry.DefaultInstance) is 
		/// </summary>
		public static bool IsRegistryCaseSensitive
		{
			get { return registry.IsCaseSensitive; }
			set { registry.IsCaseSensitive = value; }
		}

		//public static void AddLoggersToInstance(params Logger[] inLoggers)
		//{
		//    foreach (Logger logger in inLoggers)
		//        _Instance.AddChild(logger);
		//}
		//private static readonly Logger _Instance = new CompositeLogger();
		//private static Logger Instance(Level inLevel)
		//{
		//    foreach (Logger logger in _Instance.Children)
		//        logger.Level = inLevel;
		//    return _Instance;
		//}

		//dead code
		//public readonly static Level Debug = new Level(1, 1, "Debug", "debug", "DEBUG", true);

		///// <summary>
		///// A log level representing detailed information
		///// </summary>
		//public readonly static Level Verbose = new Level(2, 8, "Verbose", "verbose", "VERBOSE", true);

		///// <summary>
		///// A log level representing configuration information
		///// </summary>
		//public readonly static Level Config = new Level(3, 64, "Config", "config", "CONFIG", true);

		///// <summary>
		///// A log level representing standard informational messages
		///// </summary>
		//public readonly static Level Info = new Level(4, 512, "Info", "info", "INFO", true);

		///// <summary>
		///// A log level representing warning information
		///// </summary>
		//public readonly static Level Warning = new Level(5, 4096, "Warning", "warning", "WARNING", true);

		///// <summary>
		///// A log level representing errors and other exceptions
		///// </summary>
		//public readonly static Level Exception = new Level(6, 32768, "Exception", "exception", "EXCEPTION", true);

		///// <summary>
		///// A convenience log level representing the logging of all messages
		///// </summary>
		//public readonly static Level All = new Level(7, Int32.MinValue, "All", "all", "ALL", false);

		///// <summary>
		///// A convenience log level representing the logging of no messages whatsoever.
		///// A logger set to this level will not log
		///// </summary>
		//public readonly static Level None = new Level(8, Int32.MaxValue, "None", "none", "NONE", false);
		//public static int FrameOffset { get; set; }

		//public static Logger ForDebug
		//{
		//    get
		//    {
		//        Logger composite = Instance(Level.Debug);
		//        return composite;
		//    }
		//}
		//public static Logger ForVerbose
		//{
		//    get
		//    {
		//        Logger composite = Instance(Level.Verbose);
		//        return composite;
		//    }
		//}
		//public static Logger ForConfig
		//{
		//    get
		//    {
		//        Logger composite = Instance(Level.Config);
		//        return composite;
		//    }
		//}
		//public static Logger ForInfo
		//{
		//    get
		//    {
		//        Logger composite = Instance(Level.Info);
		//        return composite;
		//    }
		//}
		//public static Logger ForWarning
		//{
		//    get
		//    {
		//        Logger composite = Instance(Level.Warning);
		//        return composite;
		//    }
		//}
		//public static Logger ForException
		//{
		//    get
		//    {
		//        Logger composite = Instance(Level.Exception);
		//        return composite;
		//    }
		//}
		//public static Logger ForAll
		//{
		//    get
		//    {
		//        Logger composite = Instance(Level.All);
		//        return composite;
		//    }
		//}
		//public static Logger ForNone
		//{
		//    get
		//    {
		//        Logger composite = Instance(Level.None);
		//        return composite;
		//    }
		//}

		/***********************************************
		 *                                             *
		 *            BEGIN FACTORY METHODS            *
		 *                                             *
		 ***********************************************/

		public static CompositeLogger CreateCompositeLogger(params Logger[] inLoggers)
		{
			CompositeLogger logger = new CompositeLogger(inLoggers);
			return logger;
		}

		//dead code
		///// <summary>
		///// Constructs a new CompositeLogger, which will send events to each of its 
		///// children without writing anything to output itself
		///// </summary>
		///// <param name="childLogger1">A child logger</param>
		///// <param name="childLogger2">A child logger</param>
		///// <returns>A new logger</returns>
		//public static CompositeLogger CreateCompositeLogger(Logger childLogger1, Logger childLogger2) {
		//    return new CompositeLogger(childLogger1, childLogger2);
		//}
		///// <summary>
		///// Constructs a new CompositeLogger, which will send events to each of its 
		///// children without writing anything to output itself
		///// </summary>
		///// <param name="childLogger1">A child logger</param>
		///// <param name="childLogger2">A child logger</param>
		///// <param name="childLogger3">A child logger</param>
		///// <returns>A new logger</returns>
		//public static CompositeLogger CreateCompositeLogger(Logger childLogger1, Logger childLogger2, Logger childLogger3) {
		//    return new CompositeLogger(childLogger1, childLogger2, childLogger3);
		//}
		///// <summary>
		///// Constructs a new CompositeLogger, which will send events to each of its 
		///// children without writing anything to output itself
		///// </summary>
		///// <param name="childLogger1">A child logger</param>
		///// <param name="childLogger2">A child logger</param>
		///// <param name="childLogger3">A child logger</param>
		///// <param name="childLogger4">A child logger</param>
		///// <returns>A new logger</returns>
		//public static CompositeLogger CreateCompositeLogger(Logger childLogger1, Logger childLogger2, Logger childLogger3, Logger childLogger4) {
		//    return new CompositeLogger(childLogger1, childLogger2, childLogger3, childLogger4);
		//}
		///// <summary>
		///// Constructs a new CompositeLogger, which will send events to each of its 
		///// children without writing anything to output itself
		///// </summary>
		///// <param name="childLogger1">A child logger</param>
		///// <param name="childLogger2">A child logger</param>
		///// <param name="childLogger3">A child logger</param>
		///// <param name="childLogger4">A child logger</param>
		///// <param name="childLogger5">A child logger</param>
		///// <returns>A new logger</returns>
		//public static CompositeLogger CreateCompositeLogger(Logger childLogger1, Logger childLogger2, Logger childLogger3, Logger childLogger4, Logger childLogger5) {
		//    return new CompositeLogger(childLogger1, childLogger2, childLogger3, childLogger4, childLogger5);
		//}

		/// <summary>
		/// Constructs a new ConsoleLogger using the default pattern of "{timestamp}  {message}"
		/// </summary>
		/// <returns>A new logger</returns>
		public static ConsoleLogger CreateConsoleLogger()
		{
			return new ConsoleLogger();
		}
		/// <summary>
		/// Constructs a new ConsoleLogger
		/// </summary>
		/// <param name="_pattern">The pattern with which to format output</param>
		/// <returns>A new logger</returns>
		public static ConsoleLogger CreateConsoleLogger(string _pattern)
		{
			return new ConsoleLogger(_pattern);
		}

		/// <summary>
		/// Constructs a new DatabaseLogger that will use the specified connection for logging purposes.  
		/// When the logger is opened, it will open the connection
		/// </summary>
		/// <param name="_connection">The connection to use to log messages</param>
		/// <param name="_sqlPattern">The pattern to use when creating SQL statements</param>
		/// <returns>A new logger</returns>
		public static DatabaseLogger CreateDatabaseLogger(IDbConnection _connection, string _sqlPattern)
		{
			return new DatabaseLogger(_connection, _sqlPattern);
		}

		/// <summary>
		/// Constructs a new DebugLogger, which writes events to System.Diagnostics.Debug
		/// </summary>
		/// <returns>A new logger</returns>
		public static DebugLogger CreateDebugLogger()
		{
			return new DebugLogger();
		}

		/// <summary>
		/// Constructs a new EmailLogger, using the supplied parameters to construct the default email template.  
		/// The address of the local SMTP server will be used to send mail
		/// </summary>
		/// <param name="_from">The hard-coded string or pattern to use for the EmailTemplate field "From"</param>
		/// <param name="_to">The hard-coded string or pattern to use for the EmailTemplate field "To"</param>
		/// <param name="_cc">The hard-coded string or pattern to use for the EmailTemplate field "CC"</param>
		/// <param name="_bcc">The hard-coded string or pattern to use for the EmailTemplate field "BCC"</param>
		/// <param name="_subject">The hard-coded string or pattern to use for the EmailTemplate field "Subject"</param>
		/// <param name="_body">The hard-coded string or pattern to use for the EmailTemplate field "Body"</param>
		/// <returns>A new logger</returns>
		public static EmailLogger CreateEmailLogger(string _from, string _to, string _cc, string _bcc, string _subject, string _body)
		{
			return new EmailLogger(_from, _to, _cc, _bcc, _subject, _body);
		}

		/// <summary>
		/// Constructs a new EmailLogger, using the supplied parameters to construct the default email template
		/// </summary>
		/// <param name="_serverAddress">The address of the SMTP server to use</param>
		/// <param name="_from">The hard-coded string or pattern to use for the EmailTemplate field "From"</param>
		/// <param name="_to">The hard-coded string or pattern to use for the EmailTemplate field "To"</param>
		/// <param name="_cc">The hard-coded string or pattern to use for the EmailTemplate field "CC"</param>
		/// <param name="_bcc">The hard-coded string or pattern to use for the EmailTemplate field "BCC"</param>
		/// <param name="_subject">The hard-coded string or pattern to use for the EmailTemplate field "Subject"</param>
		/// <param name="_body">The hard-coded string or pattern to use for the EmailTemplate field "Body"</param>
		/// <returns>A new logger</returns>
		public static EmailLogger CreateEmailLogger(string _serverAddress, string _from, string _to, string _cc, string _bcc, string _subject, string _body)
		{
			return new EmailLogger(_serverAddress, _from, _to, _cc, _bcc, _subject, _body);
		}

		/// <summary>
		/// Constructs a new instance, using the supplied parameters to construct the default email template.  
		/// The subject pattern used is "[{levelName:case=upper}] {message:width=50}", the body pattern is "\n{message}\n{data}", 
		/// and the data formatter used is an instance of TreeDataFormatter.  
		/// The address of the local SMTP server will be used to send mail
		/// </summary>
		/// <param name="_from">The hard-coded string or pattern to use for the EmailTemplate field "From"</param>
		/// <param name="_to">The hard-coded string or pattern to use for the EmailTemplate field "To"</param>
		public EmailLogger CreateEmailLogger(string _from, string _to)
		{
			return new EmailLogger(_from, _to);
		}

		/// <summary>
		/// Constructs a new instance, using the supplied parameters to construct the default email template.  
		/// The subject pattern used is "[{levelName:case=upper}] {message:width=50}", the body pattern is "\n{message}\n{data}", 
		/// and the data formatter used is an instance of TreeDataFormatter
		/// </summary>
		/// <param name="_serverAddress">The address of the SMTP server to use</param>
		/// <param name="_from">The hard-coded string or pattern to use for the EmailTemplate field "From"</param>
		/// <param name="_to">The hard-coded string or pattern to use for the EmailTemplate field "To"</param>
		public EmailLogger CreateEmailLogger(string _serverAddress, string _from, string _to)
		{
			return new EmailLogger(_serverAddress, _from, _to);
		}

		/// <summary>
		/// Constructs a new FileLogger using the specified file path,
		/// which may be a hard-coded value or a pattern
		/// </summary>
		/// <param name="_filePath">The file path to use</param>
		/// <returns>A new logger</returns>
		public static FileLogger CreateFileLogger(string _filePath)
		{
			return new FileLogger(_filePath);
		}

		/// <summary>
		/// Constructs a new instance using the specified file path,
		/// which may be a hard-coded value or a pattern, and a pattern used
		/// in formatting Event objects
		/// </summary>
		/// <param name="_filePath">The file path to use</param>
		/// <param name="_outputPattern">The pattern to use when formatting events</param>
		/// <returns>A new logger</returns>
		public static FileLogger CreateFileLogger(string _filePath, string _outputPattern)
		{
			return new FileLogger(_filePath, _outputPattern);
		}

		/// <summary>
		/// Creates a new MessageQueueLogger with the specified server address and
		/// queue name
		/// </summary>
		/// <param name="_serverAddress">The address of the server (PERIOD for the local machine)</param>
		/// <param name="_queueName">The name of the queue to be used by the logger</param>
		/// <param name="_summaryPattern">The pattern used to construct event summaries (set on the label of each message></param>
		/// <returns>A new logger</returns>
		public static MessageQueueLogger CreateMessageQueueLogger(string _serverAddress, string _queueName, string _summaryPattern)
		{
			return new MessageQueueLogger(_serverAddress, _queueName, _summaryPattern);
		}

		/// <summary>
		/// Creates a new MessageQueueLogger with the specified server address and
		/// queue name
		/// </summary>
		/// <param name="_serverAddress">The address of the server (PERIOD for the local machine)</param>
		/// <param name="_queueName">The name of the queue to be used by the logger</param>
		/// <returns>A new logger</returns>
		public static MessageQueueLogger CreateMessageQueueLogger(string _serverAddress, string _queueName)
		{
			return new MessageQueueLogger(_serverAddress, _queueName);
		}

		/// <summary>
		/// Creates a new MessageQueueLogger with the specified queue name; it will log to the
		/// machine hosting the process, which must be providing MSMQ services
		/// </summary>
		/// <param name="_queueName">The name of the queue to be used by the logger</param>
		public static MessageQueueLogger CreateMessageQueueLogger(string _queueName)
		{
			return new MessageQueueLogger(_queueName);
		}

		/// <summary>
		/// Constructs a new instance using an XMLEventFormatter
		/// </summary>
		/// <param name="_stream">The stream to which to write</param>
		/// <returns>A new logger</returns>
		public static StreamLogger CreateStreamLogger(Stream _stream)
		{
			return new StreamLogger(_stream);
		}

		/// <summary>
		/// Constructs a new TraceLogger, which writes events to System.Diagnostics.Trace
		/// </summary>
		/// <returns>A new logger</returns>
		public static TraceLogger CreateTraceLogger()
		{
			return new TraceLogger();
		}

		/// <summary>
		/// Constructs a new WindowsEventLogger, which will write to a Windows event log 
		/// with the specified name
		/// </summary>
		/// <param name="_eventLogApplicationName">The Windows event log name</param>
		/// <returns>A new logger</returns>
		//public static WindowsEventLogger CreateWindowsEventLogger(string _eventLogApplicationName) {
		//    return new WindowsEventLogger(_eventLogApplicationName);
		//}
		public static WindowsEventLogger CreateWindowsEventLogger(string _eventLogSource, string _eventLogName)
		{
			return new WindowsEventLogger(_eventLogSource, _eventLogName);
		}

		/***********************************************
		 *                                             *
		 *             END FACTORY METHODS             *
		 *                                             *
		 ***********************************************/


		public void RegisterAs(string name)
		{
			if (name == null)
			{
				throw new ArgumentNullException();
			}
			lock (monitor)
			{
				if (isClosed)
				{
					Open();
				}
				registry.AddLogger(name, this);
				registrations.Add(name + "_" + registry.GetHashCode(), new Registration(name, registry));
			}
		}

		public void RegisterAs(string name, LoggerRegistry _registry)
		{
			if (name == null)
			{
				throw new ArgumentNullException();
			}
			lock (monitor)
			{
				if (isClosed)
				{
					Open();
				}
				_registry.AddLogger(name, this);
				registrations.Add(name + "_" + _registry.GetHashCode(), new Registration(name, _registry));
			}
		}

		public void Deregister()
		{
			lock (monitor)
			{
				Registration registration;
				IEnumerator e = registrations.Keys.GetEnumerator();
				while (e.MoveNext())
				{
					registration = (Registration)registrations[e.Current];
					registration.Registry.RemoveLogger(this);
				}
				registrations.Clear();
			}
		}

		//protected Logger() 
		//{
		//    FrameOffset = 2;
		//}
		protected Logger()
		{
			//FrameOffset = 2;
		}

		/// <summary>
		/// Adds a child Logger to this instance; each Event created by a Logger is passed
		/// to its children
		/// </summary>
		/// <param name="child">The logger to add as a child</param>
		public void AddChild(Logger child)
		{
			if (child == null)
			{
				throw new ArgumentNullException();
			}
			lock (childMonitor)
			{
				for (int x = 0; x < childCount; x++)
				{
					if (children[x] == child)
					{
						return;
					}
				}
				if ((!isClosed) && (!child.IsOpen))
				{
					child.Open();
				}
				if (childCount == children.Length)
				{
					Logger[] newChildren = new Logger[childCount * 2];
					Array.Copy(children, 0, newChildren, 0, childCount);
					children = newChildren;
				}
				children[childCount++] = child;
			}
		}

		/// <summary>
		/// Removes a child logger from this instance, so it will no longer receive hand-me-down
		/// messages from this instance.  See AddChile(Logger)
		/// </summary>
		/// <param name="child">The logger to remove from the collection of children</param>
		public void RemoveChild(Logger child)
		{
			if (child == null)
			{
				throw new ArgumentNullException();
			}
			lock (childMonitor)
			{
				for (int x = 0; x < childCount; x++)
				{
					if (children[x] == child)
					{
						if (x < (childCount - 1))
						{
							Array.Copy(children, (x + 1), children, x, ((childCount - x) - 1));
						}
						children[--childCount] = null;
						return;
					}
				}
			}
		}

		/// <summary>
		/// Removes all child loggers from this instance
		/// </summary>
		public void ClearChildren()
		{
			lock (childMonitor)
			{
				for (int x = 0; x < childCount; x++)
				{
					children[x] = null;
				}
				childCount = 0;
			}
		}

		/// <summary>
		/// Indicates whether the specified logger is a child of this instance (receives passed events)
		/// </summary>
		/// <param name="child">The logger to check for in the collection of this logger's children</param>
		/// <returns>true if the specified logger is this logger's child, false otherwise</returns>
		public bool HasChild(Logger child)
		{
			if (child == null)
			{
				throw new ArgumentNullException();
			}
			lock (childMonitor)
			{
				for (int x = 0; x < childCount; x++)
				{
					if (children[x] == child)
					{
						return true;
					}
				}
				return false;
			}
		}

		/// <summary>
		/// Gets the number of child loggers associated with this instance
		/// </summary>
		public int ChildCount
		{
			get
			{
				lock (childMonitor)
				{
					return childCount;
				}
			}
		}

		/// <summary>
		/// Gets an array of all child loggers associated with this instance
		/// </summary>
		public Logger[] Children
		{
			get
			{
				lock (childMonitor)
				{
					Logger[] returnValue = new Logger[childCount];
					if (childCount > 0)
						Array.Copy(children, 0, returnValue, 0, childCount);
					return returnValue;
				}
			}
		}

		/// <summary>
		/// Provides index-based access to child loggers associated with this instance
		/// </summary>
		/// <param name="index">The index for which to retrieve the child logger</param>
		/// <returns>The logger at the specified index</returns>
		public Logger GetChildAt(int index)
		{
			if (index < 0)
				throw new IndexOutOfRangeException();
			lock (childMonitor)
			{
				if (index > childCount)
					throw new IndexOutOfRangeException();
				return children[index];
			}
		}

		/// <summary>
		/// Gets and sets the System.Text.Encoding used when writing messages to output
		/// </summary>
		public Encoding Encoding
		{
			get { return encoding; }
			set
			{
				if (value == null)
					throw new ArgumentNullException();
				lock (monitor)
				{
					if (UpdateEncoding(value))
						encoding = value;
					else
						throw new ApplicationException("Invalid encoding");
				}
			}
		}

		protected virtual bool UpdateEncoding(Encoding _encoding)
		{
			return true;
		}

		/// <summary>
		/// Gets and sets the Filter object associated with this instance.  The Filter
		/// is used to reject certain events from output based on selection criteria; see the Filter
		/// class and the contents of the NSpring.Logging.Filters namespace for more details.<br>
		/// Each Logger is created with no associated Filter by default, and can only accept one Filter.
		/// In order to add more detailed filtering logic, create a Filter subclass or use CompositeFilter.
		/// </summary>
		public Filter Filter
		{
			get { return filter; }
			set { filter = value; }
		}

		/// <summary>
		/// Gets and sets the EventFormatter object associated with this instance.  Each predefined Logger
		/// subclass has a default EventFormatter that can be replaced, if desired; the only requirement
		/// is that an EventFormatter must be set before calling the logger's Open() method.  The event
		/// formatter can be switched on an open logger, but not to a null reference.
		/// </summary>
		public EventFormatter EventFormatter
		{
			get
			{
				lock (monitor) { return eventFormatter; }
			}
			set
			{
				if (value == null)
					throw new ArgumentNullException();

				lock (monitor)
				{
					if (UpdateEventFormatter(value))
					{
						value.Logger = this;
						eventFormatter = value;
					}
					else
						throw new ApplicationException("Invalid event formatter");
				}
			}
		}

		protected virtual bool UpdateEventFormatter(EventFormatter _eventFormatter)
		{
			return true;
		}

		/// <summary>
		/// Gets and sets the exception handler used by all loggers by default
		/// </summary>
		public static IExceptionHandler DefaultExceptionHandler
		{
			get
			{
				return defaultExceptionHandler;
			}
			set
			{
				if (value == null)
				{
					defaultExceptionHandler = new SilentExceptionHandler();
				}
				else
				{
					defaultExceptionHandler = value;
				}
			}
		}

		/// <summary>
		/// Gets and sets the exception handler for this logger
		/// </summary>
		public IExceptionHandler ExceptionHandler
		{
			get
			{
				return exceptionHandler;
			}
			set
			{
				if (value == null)
				{
					exceptionHandler = new SilentExceptionHandler();
				}
				else if (value == this)
				{
					throw new ArgumentException("A logger cannot handle its own exceptions");
				}
				else
				{
					lock (monitor)
					{
						if (!isClosed)
						{
							try
							{
								value.Open();
							}
							catch (Exception e)
							{
								throw new ApplicationException("Could not open the exception handler: " + e.GetType().FullName + ": " + e.Message);
							}
						}
						exceptionHandler = value;
					}
				}
			}
		}
		/// <summary>
		/// Gets and sets the logger's level.  Any messages logged with a lower level will not be logged.
		/// The default is Level.ALL, so the default behavior is to log all messages
		/// </summary>
		public Level Level
		{
			get { return level; }
			set
			{
				if (value == null)
					throw new ArgumentNullException();

				lock (monitor)
				{
					level = value;
					levelPriority = level.Priority;
				}
			}
		}

		/// <summary>
		/// A convenience property that lets application code avoid the cost of parameter
		/// construction if the logger's turned off, with a simple easy check
		/// </summary>
		public bool IsLoggingEnabled { get { return ((!isClosed) && (level != Level.None)); } }

		/// <summary>
		/// Gets and sets the default level applied to messages logged by this logger.  If a Log() method
		/// is called which doesn't take a Level as a parameter, this is the level of the resulting Event
		/// object.  The default is Level.Info
		/// </summary>
		public Level DefaultEventLevel
		{
			get { return defaultEventLevel; }
			set
			{
				if (value == null)
				{
					throw new ArgumentNullException();
				}
				defaultEventLevel = value;
			}
		}

		/// <summary>
		/// Gets and sets whether context logging is enabled for loggers by default.
		/// See the NSpring.Logging.Context class for more details.
		/// </summary>
		public static bool IsContextEnabledByDefault
		{
			get { return isContextEnabledByDefault; }
			set { isContextEnabledByDefault = value; }
		}

		/// <summary>
		/// Gets and sets whether context logging is enabled for this logger.
		/// See the NSpring.Logging.Context class for more details.
		/// </summary>
		public bool IsContextEnabled
		{
			get { return isContextEnabled; }
			set { isContextEnabled = value; }
		}

		/// <summary>
		/// Gets/sets whether this Logger will automatically create 32-character hex IDs for all logged
		/// messages.  The default is false
		/// </summary>
		public bool IsIDGenerationEnabled
		{
			get
			{
				return isIDGenerationEnabled;
			}
			set
			{
				isIDGenerationEnabled = value;
			}
		}

		/// <summary>
		/// If the value of the expression passed as the first argument is false, logs a 
		/// message at the logger's default level
		/// </summary>
		/// <param name="expression">The expression to evaluate</param>
		/// <param name="failureMessage">The message to log if the expression is false</param>
		public void Assert(bool expression, string failureMessage)
		{
			if (failureMessage == null)
			{
				return;
			}
			if (!expression)
			{
				LogEvent(defaultEventLevel, failureMessage, null, null, null, null);
			}
		}

		/// <summary>
		/// If the value of the expression passed as the first argument is false, logs a 
		/// message at the specified level
		/// </summary>
		/// <param name="_level">The level at which to log the message</param>
		/// <param name="expression">The expression to evaluate</param>
		/// <param name="failureMessage">The message to log if the expression is false</param>
		public void Assert(Level _level, bool expression, string failureMessage)
		{
			if (failureMessage == null)
			{
				return;
			}
			if (!expression)
			{
				LogEvent(((level == null) ? defaultEventLevel : _level), failureMessage, null, null, null, null);
			}
		}

		/// <summary>
		/// If the value of the expression passed as the first argument is false, logs a 
		/// message at the specified level
		/// </summary>
		/// <param name="_level">The level at which to log the message</param>
		/// <param name="expression">The expression to evaluate</param>
		/// <param name="failureMessage">The message to log if the expression is false</param>
		/// <param name="successMessage">The message to log if the expression is true</param>
		public void Assert(Level _level, bool expression, string failureMessage, string successMessage)
		{
			if (expression)
			{
				if (successMessage == null)
				{
					return;
				}
				LogEvent(((level == null) ? defaultEventLevel : _level), successMessage, null, null, null, null);
			}
			else
			{
				if (failureMessage == null)
				{
					return;
				}
				LogEvent(((level == null) ? defaultEventLevel : _level), failureMessage, null, null, null, null);
			}
		}

		/// <summary>
		/// If the value of the expression passed as the first argument is false, logs a 
		/// message at the logger's default level
		/// </summary>
		/// <param name="expression">The expression to evaluate</param>
		/// <param name="failureMessage">The message to log if the expression is false</param>
		/// <param name="successMessage">The message to log if the expression is true</param>
		public void Assert(bool expression, string failureMessage, string successMessage)
		{
			if (expression)
			{
				if (successMessage == null)
				{
					return;
				}
				LogEvent(defaultEventLevel, successMessage, null, null, null, null);
			}
			else
			{
				if (failureMessage == null)
				{
					return;
				}
				LogEvent(defaultEventLevel, failureMessage, null, null, null, null);
			}
		}


		//public Logger LogMethodInfo()
		//{
		//    LogMethodInfo("", FrameOffset);
		//    return Instance(Level);
		//}
		//public Logger LogMethodInfo(string inText)
		//{
		//    LogMethodInfo(inText, FrameOffset);
		//    return Instance(Level);
		//}
		//public Logger LogMethodInfo(int inFrameOffset)
		//{
		//    LogMethodInfo("", inFrameOffset);
		//    return Instance(Level);
		//}
		//public Logger LogMethodInfo(string inText, int inFrameOffset)
		//{
		//    StackFrame sf = new StackTrace(true).GetFrame(inFrameOffset);
		//    Log(string.Format("{0} {1}::{2} in {3} at [line:{4},char:{5}]", new object[] { inText, sf.GetMethod().DeclaringType.Name, sf.GetMethod().Name, Path.GetFileName(sf.GetFileName()), sf.GetFileLineNumber(), sf.GetFileColumnNumber() }));

		//    return Instance(Level);
		//}
		//public Logger LogProcessInfo()
		//{
		//    LogProcessInfo(Process.GetCurrentProcess());
		//    return Instance(Level); ;
		//}
		//public Logger LogProcessInfo(Process inProcess)
		//{
		//    Log(FormatMyProcess(inProcess));
		//    return Instance(Level); ;
		//}
		//private static string FormatMyProcess(Process inProcess)
		//{
		//    string str = "";
		//    if (inProcess != null)
		//    {
		//        str += "\nProcess Info:";
		//        str += "\n{";
		//        str += "\n\tMemory: " + inProcess.WorkingSet64 / 1024 + "k";
		//        str += "\n\tPeak: " + inProcess.PeakWorkingSet64 / 1024 + "k";
		//        str += "\n\tThreads: " + inProcess.Threads.Count;
		//        str += "\n\tMainModule: " + Path.GetFileNameWithoutExtension(inProcess.MainModule.FileName) + " version: " + inProcess.MainModule.FileVersionInfo.FileVersion;
		//        str += "\n}";
		//    }
		//    return str;
		//}
		//public Logger NewLine()
		//{
		//    NewLine(1);
		//    return Instance(Level);
		//}
		//public Logger NewLine(uint inNumber)
		//{
		//    string newlines = (inNumber > 0) ? "\n" : "";
		//    for (int i = 1; i < inNumber; ++i)
		//        newlines += "\n";
		//    Log(newlines);
		//    return Instance(Level);
		//}
		//public Logger Tab()
		//{
		//    Tab(1);
		//    return Instance(Level);
		//}
		//public Logger Tab(uint inNumber)
		//{
		//    string tabs = (inNumber > 0) ? "\t" : "";
		//    for (int i = 1; i < inNumber; ++i)
		//        tabs += "\t";
		//    Log(tabs);
		//    return Instance(Level);
		//}

		/// <summary>
		/// Logs a message (creates an Event) using the specified message, the default level, and the
		/// logger's ApplicationName and SystemName properties
		/// </summary>
		/// <param name="_message">The message to log</param>
		public void Log(string _message)
		{
			Context context;
			if (isContextEnabled)
			{
				context = Context.CurrentContext.Copy();
			}
			else
			{
				context = null;
			}
			long ticks = DateTime.Now.Ticks;
			Monitor.Enter(monitor);
			//        lock (monitor) {
			if (isClosed)
			{
				Monitor.Exit(monitor);
				throw new ApplicationException("This logger is closed");
			}
			if (isBufferingEnabled)
			{
				if (bufferCount < bufferSize)
				{
					if (context != null)
					{
						contextBuffer[bufferCount] = context;
					}
					levelBuffer[bufferCount] = defaultEventLevel;
					timestampBuffer[bufferCount] = ticks;
					messageBuffer[bufferCount] = _message;
					bufferCount++;
					Monitor.Exit(monitor);
					return;
				}
				else
				{
					if (bufferCount >= maximumBufferSize)
					{
						Monitor.Exit(monitor);
						return;
					}
					else if (bufferCount == contextBuffer.Length)
					{
						int newSize = contextBuffer.Length * 2;

						Context[] newContextBuffer = new Context[newSize];
						Level[] newLevelBuffer = new Level[newSize];
						long[] newTimestampBuffer = new long[newSize];
						string[] newMessageBuffer = new string[newSize];
						object[] newDataBuffer = new object[newSize];
						string[] newCategoryBuffer = new string[newSize];
						string[] newCodeBuffer = new string[newSize];
						string[] newIDBuffer = new string[newSize];

						Array.Copy(contextBuffer, 0, newContextBuffer, 0, bufferCount);
						Array.Copy(levelBuffer, 0, newLevelBuffer, 0, bufferCount);
						Array.Copy(timestampBuffer, 0, newTimestampBuffer, 0, bufferCount);
						Array.Copy(messageBuffer, 0, newMessageBuffer, 0, bufferCount);
						Array.Copy(dataBuffer, 0, newDataBuffer, 0, bufferCount);
						Array.Copy(categoryBuffer, 0, newCategoryBuffer, 0, bufferCount);
						Array.Copy(codeBuffer, 0, newCodeBuffer, 0, bufferCount);
						Array.Copy(idBuffer, 0, newIDBuffer, 0, bufferCount);

						contextBuffer = newContextBuffer;
						levelBuffer = newLevelBuffer;
						timestampBuffer = newTimestampBuffer;
						messageBuffer = newMessageBuffer;
						dataBuffer = newDataBuffer;
						categoryBuffer = newCategoryBuffer;
						codeBuffer = newCodeBuffer;
						idBuffer = newIDBuffer;

						contextBuffer[bufferCount] = context;
						levelBuffer[bufferCount] = defaultEventLevel;
						timestampBuffer[bufferCount] = ticks;
						messageBuffer[bufferCount] = _message;
						bufferCount++;
						if (bufferCount == bufferSize)
						{
							flushingThread.Awaken();
						}
						Monitor.Exit(monitor);
						return;
					}
					else
					{
						contextBuffer[bufferCount] = context;
						levelBuffer[bufferCount] = defaultEventLevel;
						timestampBuffer[bufferCount] = ticks;
						messageBuffer[bufferCount] = _message;
						bufferCount++;
						if (bufferCount == bufferSize)
						{
							flushingThread.Awaken();
						}
						Monitor.Exit(monitor);
						return;
					}
				}
			}
			else
			{
				string _id = null;
				if (isIDGenerationEnabled)
				{
					// This may look dorky, but using the System.Guid class
					// is three times as slow, and produces bad-looking hex strings to boot
					random1.NextBytes(byteArray1);
					charArray1[0] = HEX_CHARS[byteArray1[0] & 15];
					charArray1[1] = HEX_CHARS[byteArray1[0] >> 4];
					charArray1[2] = HEX_CHARS[byteArray1[1] & 15];
					charArray1[3] = HEX_CHARS[byteArray1[1] >> 4];
					charArray1[4] = HEX_CHARS[byteArray1[2] & 15];
					charArray1[5] = HEX_CHARS[byteArray1[2] >> 4];
					charArray1[6] = HEX_CHARS[byteArray1[3] & 15];
					charArray1[7] = HEX_CHARS[byteArray1[3] >> 4];
					charArray1[8] = HEX_CHARS[byteArray1[4] & 15];
					charArray1[9] = HEX_CHARS[byteArray1[4] >> 4];
					charArray1[10] = HEX_CHARS[byteArray1[5] & 15];
					charArray1[11] = HEX_CHARS[byteArray1[5] >> 4];
					charArray1[12] = HEX_CHARS[byteArray1[6] & 15];
					charArray1[13] = HEX_CHARS[byteArray1[6] >> 4];
					charArray1[14] = HEX_CHARS[byteArray1[7] & 15];
					charArray1[15] = HEX_CHARS[byteArray1[7] >> 4];
					charArray1[16] = HEX_CHARS[byteArray1[8] & 15];
					charArray1[17] = HEX_CHARS[byteArray1[8] >> 4];
					charArray1[18] = HEX_CHARS[byteArray1[9] & 15];
					charArray1[19] = HEX_CHARS[byteArray1[9] >> 4];
					charArray1[20] = HEX_CHARS[byteArray1[10] & 15];
					charArray1[21] = HEX_CHARS[byteArray1[10] >> 4];
					charArray1[22] = HEX_CHARS[byteArray1[11] & 15];
					charArray1[23] = HEX_CHARS[byteArray1[11] >> 4];
					charArray1[24] = HEX_CHARS[byteArray1[12] & 15];
					charArray1[25] = HEX_CHARS[byteArray1[12] >> 4];
					charArray1[26] = HEX_CHARS[byteArray1[13] & 15];
					charArray1[27] = HEX_CHARS[byteArray1[13] >> 4];
					charArray1[28] = HEX_CHARS[byteArray1[14] & 15];
					charArray1[29] = HEX_CHARS[byteArray1[14] >> 4];
					charArray1[30] = HEX_CHARS[byteArray1[15] & 15];
					charArray1[31] = HEX_CHARS[byteArray1[15] >> 4];
					_id = new string(charArray1);
				}
				Event ev = new Event(new DateTime(ticks), defaultEventLevel, _message, null, null, null, _id);
				if (filter != null)
				{
					Filter f = filter;
					Monitor.Exit(monitor);
					try
					{
						if (!filter.Accepts(ev))
						{
							return;
						}
					}
					catch { }
					Monitor.Enter(monitor);
					if (isClosed)
					{
						return;
					}
				}
				try
				{
					Write(ev);
				}
				catch (Exception e)
				{
					exceptionHandler.Handle(new LoggerException(this, ev, e));
				}
				if (childCount > 0)
				{
					Monitor.Enter(childMonitor);
					//                    lock (childMonitor) {
					Monitor.Exit(monitor);
					for (int x = 0; x < childCount; x++)
					{
						children[x].LogEvent(defaultEventLevel, _message, null, null, null, _id);
					}
					Monitor.Exit(childMonitor);
					//                    }
				}
				else
				{
					Monitor.Exit(monitor);
				}
			}
			//        }
		}

		/// <summary>
		/// Logs a message (creates an Event) using the specified message, the default level, and the
		/// logger's ApplicationName and SystemName properties
		/// </summary>
		/// <param name="_message">The message to log</param>
		/// <param name="_category">The category of the message</param>
		public void Log(string _message, string _category)
		{
			if (_message == null)
			{
				return;
			}
			LogEvent(defaultEventLevel, _message, null, _category, null, null);
		}

		/// <summary>
		/// Logs a message (creates an Event) using the specified message, the specified level, and the
		/// logger's ApplicationName and SystemName properties
		/// </summary>
		/// <param name="_level">The Level to apply to the message, which may result in the message being 
		/// withheld from logging</param>
		/// <param name="_message">The message to log</param>
		public void Log(Level _level, string _message)
		{
			if (_message == null)
			{
				return;
			}
			if (_level == null)
			{
				_level = defaultEventLevel;
			}
			if (_level.Priority < levelPriority)
			{
				return;
			}
			Context context;
			if (isContextEnabled)
			{
				context = Context.CurrentContext.Copy();
			}
			else
			{
				context = null;
			}
			long ticks = DateTime.Now.Ticks;
			Monitor.Enter(monitor);
			//        lock (monitor) {
			if (isClosed)
			{
				Monitor.Exit(monitor);
				throw new ApplicationException("This logger is closed");
			}
			if (isBufferingEnabled)
			{
				if (bufferCount < bufferSize)
				{
					contextBuffer[bufferCount] = context;
					levelBuffer[bufferCount] = _level;
					timestampBuffer[bufferCount] = ticks;
					messageBuffer[bufferCount] = _message;
					bufferCount++;
					Monitor.Exit(monitor);
				}
				else
				{
					if (bufferCount >= maximumBufferSize)
					{
						Monitor.Exit(monitor);
						return;
					}
					else if (bufferCount == contextBuffer.Length)
					{
						int newSize = contextBuffer.Length * 2;

						Context[] newContextBuffer = new Context[newSize];
						Level[] newLevelBuffer = new Level[newSize];
						long[] newTimestampBuffer = new long[newSize];
						string[] newMessageBuffer = new string[newSize];
						object[] newDataBuffer = new object[newSize];
						string[] newCategoryBuffer = new string[newSize];
						string[] newCodeBuffer = new string[newSize];
						string[] newIDBuffer = new string[newSize];

						Array.Copy(contextBuffer, 0, newContextBuffer, 0, bufferCount);
						Array.Copy(levelBuffer, 0, newLevelBuffer, 0, bufferCount);
						Array.Copy(timestampBuffer, 0, newTimestampBuffer, 0, bufferCount);
						Array.Copy(messageBuffer, 0, newMessageBuffer, 0, bufferCount);
						Array.Copy(dataBuffer, 0, newDataBuffer, 0, bufferCount);
						Array.Copy(categoryBuffer, 0, newCategoryBuffer, 0, bufferCount);
						Array.Copy(codeBuffer, 0, newCodeBuffer, 0, bufferCount);
						Array.Copy(idBuffer, 0, newIDBuffer, 0, bufferCount);

						contextBuffer = newContextBuffer;
						levelBuffer = newLevelBuffer;
						timestampBuffer = newTimestampBuffer;
						messageBuffer = newMessageBuffer;
						dataBuffer = newDataBuffer;
						categoryBuffer = newCategoryBuffer;
						codeBuffer = newCodeBuffer;
						idBuffer = newIDBuffer;

						contextBuffer[bufferCount] = context;
						levelBuffer[bufferCount] = _level;
						timestampBuffer[bufferCount] = ticks;
						messageBuffer[bufferCount] = _message;
						bufferCount++;
						if (bufferCount == bufferSize)
						{
							flushingThread.Awaken();
						}
						Monitor.Exit(monitor);
					}
					else
					{
						contextBuffer[bufferCount] = context;
						levelBuffer[bufferCount] = _level;
						timestampBuffer[bufferCount] = ticks;
						messageBuffer[bufferCount] = _message;
						bufferCount++;
						if (bufferCount == bufferSize)
						{
							flushingThread.Awaken();
						}
						Monitor.Exit(monitor);
					}
				}
			}
			else
			{
				string _id = null;
				if (isIDGenerationEnabled)
				{
					// This may look dorky, but using the System.Guid class
					// is three times as slow, and produces bad-looking hex strings to boot
					random1.NextBytes(byteArray1);
					charArray1[0] = HEX_CHARS[byteArray1[0] & 15];
					charArray1[1] = HEX_CHARS[byteArray1[0] >> 4];
					charArray1[2] = HEX_CHARS[byteArray1[1] & 15];
					charArray1[3] = HEX_CHARS[byteArray1[1] >> 4];
					charArray1[4] = HEX_CHARS[byteArray1[2] & 15];
					charArray1[5] = HEX_CHARS[byteArray1[2] >> 4];
					charArray1[6] = HEX_CHARS[byteArray1[3] & 15];
					charArray1[7] = HEX_CHARS[byteArray1[3] >> 4];
					charArray1[8] = HEX_CHARS[byteArray1[4] & 15];
					charArray1[9] = HEX_CHARS[byteArray1[4] >> 4];
					charArray1[10] = HEX_CHARS[byteArray1[5] & 15];
					charArray1[11] = HEX_CHARS[byteArray1[5] >> 4];
					charArray1[12] = HEX_CHARS[byteArray1[6] & 15];
					charArray1[13] = HEX_CHARS[byteArray1[6] >> 4];
					charArray1[14] = HEX_CHARS[byteArray1[7] & 15];
					charArray1[15] = HEX_CHARS[byteArray1[7] >> 4];
					charArray1[16] = HEX_CHARS[byteArray1[8] & 15];
					charArray1[17] = HEX_CHARS[byteArray1[8] >> 4];
					charArray1[18] = HEX_CHARS[byteArray1[9] & 15];
					charArray1[19] = HEX_CHARS[byteArray1[9] >> 4];
					charArray1[20] = HEX_CHARS[byteArray1[10] & 15];
					charArray1[21] = HEX_CHARS[byteArray1[10] >> 4];
					charArray1[22] = HEX_CHARS[byteArray1[11] & 15];
					charArray1[23] = HEX_CHARS[byteArray1[11] >> 4];
					charArray1[24] = HEX_CHARS[byteArray1[12] & 15];
					charArray1[25] = HEX_CHARS[byteArray1[12] >> 4];
					charArray1[26] = HEX_CHARS[byteArray1[13] & 15];
					charArray1[27] = HEX_CHARS[byteArray1[13] >> 4];
					charArray1[28] = HEX_CHARS[byteArray1[14] & 15];
					charArray1[29] = HEX_CHARS[byteArray1[14] >> 4];
					charArray1[30] = HEX_CHARS[byteArray1[15] & 15];
					charArray1[31] = HEX_CHARS[byteArray1[15] >> 4];
					_id = new string(charArray1);
				}
				Event ev = new Event(new DateTime(ticks), _level, _message, null, null, null, _id);
				if (filter != null)
				{
					Filter f = filter;
					Monitor.Exit(monitor);
					try
					{
						if (!filter.Accepts(ev))
						{
							return;
						}
					}
					catch { }
					Monitor.Enter(monitor);
					if (isClosed)
					{
						return;
					}
				}
				try
				{
					Write(ev);
				}
				catch (Exception e)
				{
					exceptionHandler.Handle(new LoggerException(this, ev, e));
				}
				if (childCount > 0)
				{
					Monitor.Enter(childMonitor);
					//                    lock (childMonitor) {
					Monitor.Exit(monitor);
					for (int x = 0; x < childCount; x++)
					{
						children[x].LogEvent(_level, _message, null, null, null, _id);
					}
					Monitor.Exit(childMonitor);
					//                    }
				}
				else
				{
					Monitor.Exit(monitor);
				}
			}
			//        }
		}

		/// <summary>
		/// Logs a message (creates an Event) using the specified message, the specified level, and the
		/// logger's ApplicationName and SystemName properties
		/// </summary>
		/// <param name="_level">The Level to apply to the message, which may result in the message being 
		/// withheld from logging</param>
		/// <param name="_message">The message to log</param>
		/// <param name="_category">The category of the message</param>
		public void Log(Level _level, string _message, string _category)
		{
			if (_message == null)
			{
				return;
			}
			LogEvent(((_level == null) ? defaultEventLevel : _level), _message, null, _category, null, null);
		}

		/// <summary>
		/// Logs a message (creates an Event) using the specified message, event category, and event code
		/// </summary>
		/// <param name="_message">The message associated with the event</param>
		/// <param name="_category">The category of the application event (can be null)</param>
		/// <param name="_code">The event code of the application event (can be null)</param>
		public void Log(string _message, string _category, string _code)
		{
			if (_message == null)
			{
				return;
			}
			LogEvent(defaultEventLevel, _message, null, _category, _code, null);
		}

		/// <summary>
		/// Logs a message (creates an Event) using the specified message, event category, and event code
		/// </summary>
		/// <param name="_message">The message associated with the event</param>
		/// <param name="_category">The category of the application event (can be null)</param>
		/// <param name="_code">The event code of the application event (can be null)</param>
		/// <param name="_id">The ID of the application event (can be null)</param>
		public void Log(string _message, string _category, string _code, string _id)
		{
			if (_message == null)
			{
				return;
			}
			LogEvent(defaultEventLevel, _message, null, _category, _code, _id);
		}

		/// <summary>
		/// Logs a message (creates an Event) using the specified level, message, event category, and event code
		/// </summary>
		/// <param name="_level">The Level to apply to the event</param>
		/// <param name="_message">The message associated with the event</param>
		/// <param name="_category">The category of the application event (can be null)</param>
		/// <param name="_code">The code of the application event (can be null)</param>
		public void Log(Level _level, string _message, string _category, string _code)
		{
			if (_message == null)
			{
				return;
			}
			LogEvent(((_level == null) ? defaultEventLevel : _level), _message, null, _category, _code, null);
		}

		/// <summary>
		/// Logs a message (creates an Event) using the specified level, message, event category, and event code
		/// </summary>
		/// <param name="_level">The Level to apply to the event</param>
		/// <param name="_message">The message associated with the event</param>
		/// <param name="_category">The category of the application event (can be null)</param>
		/// <param name="_code">The code of the application event (can be null)</param>
		/// <param name="_id">The ID of the application event (can be null)</param>
		public void Log(Level _level, string _message, string _category, string _code, string _id)
		{
			if (_message == null)
			{
				return;
			}
			LogEvent(((_level == null) ? defaultEventLevel : _level), _message, null, _category, _code, _id);
		}

		/// <summary>
		/// Creates an Event with the specified string message and object payload
		/// </summary>
		/// <param name="_message">The string message to log</param>
		/// <param name="_data">The data payload</param>
		public void Log(string _message, object _data)
		{
			if ((_message == null) && (_data == null))
			{
				return;
			}
			if (defaultEventLevel.Priority < levelPriority)
			{
				return;
			}
			Context context;
			if (isContextEnabled)
			{
				context = Context.CurrentContext.Copy();
			}
			else
			{
				context = null;
			}
			long ticks = DateTime.Now.Ticks;
			Monitor.Enter(monitor);
			//        lock (monitor) {
			if (isClosed)
			{
				Monitor.Exit(monitor);
				throw new ApplicationException("This logger is closed");
			}
			if (isBufferingEnabled)
			{
				if (bufferCount < bufferSize)
				{
					contextBuffer[bufferCount] = context;
					levelBuffer[bufferCount] = defaultEventLevel;
					timestampBuffer[bufferCount] = ticks;
					messageBuffer[bufferCount] = _message;
					dataBuffer[bufferCount] = _data;
					bufferCount++;
					Monitor.Exit(monitor);
				}
				else
				{
					if (bufferCount >= maximumBufferSize)
					{
						Monitor.Exit(monitor);
						return;
					}
					else if (bufferCount == contextBuffer.Length)
					{
						int newSize = contextBuffer.Length * 2;

						Context[] newContextBuffer = new Context[newSize];
						Level[] newLevelBuffer = new Level[newSize];
						long[] newTimestampBuffer = new long[newSize];
						string[] newMessageBuffer = new string[newSize];
						object[] newDataBuffer = new object[newSize];
						string[] newCategoryBuffer = new string[newSize];
						string[] newCodeBuffer = new string[newSize];
						string[] newIDBuffer = new string[newSize];

						Array.Copy(contextBuffer, 0, newContextBuffer, 0, bufferCount);
						Array.Copy(levelBuffer, 0, newLevelBuffer, 0, bufferCount);
						Array.Copy(timestampBuffer, 0, newTimestampBuffer, 0, bufferCount);
						Array.Copy(messageBuffer, 0, newMessageBuffer, 0, bufferCount);
						Array.Copy(dataBuffer, 0, newDataBuffer, 0, bufferCount);
						Array.Copy(categoryBuffer, 0, newCategoryBuffer, 0, bufferCount);
						Array.Copy(codeBuffer, 0, newCodeBuffer, 0, bufferCount);
						Array.Copy(idBuffer, 0, newIDBuffer, 0, bufferCount);

						contextBuffer = newContextBuffer;
						levelBuffer = newLevelBuffer;
						timestampBuffer = newTimestampBuffer;
						messageBuffer = newMessageBuffer;
						dataBuffer = newDataBuffer;
						categoryBuffer = newCategoryBuffer;
						codeBuffer = newCodeBuffer;
						idBuffer = newIDBuffer;

						contextBuffer[bufferCount] = context;
						levelBuffer[bufferCount] = defaultEventLevel;
						timestampBuffer[bufferCount] = ticks;
						messageBuffer[bufferCount] = _message;
						dataBuffer[bufferCount] = _data;
						bufferCount++;
						if (bufferCount == bufferSize)
						{
							flushingThread.Awaken();
						}
						Monitor.Exit(monitor);
					}
					else
					{
						contextBuffer[bufferCount] = context;
						levelBuffer[bufferCount] = defaultEventLevel;
						timestampBuffer[bufferCount] = ticks;
						messageBuffer[bufferCount] = _message;
						dataBuffer[bufferCount] = _data;
						bufferCount++;
						if (bufferCount == bufferSize)
						{
							flushingThread.Awaken();
						}
						Monitor.Exit(monitor);
					}
				}
			}
			else
			{
				string _id = null;
				if (isIDGenerationEnabled)
				{
					// This may look dorky, but using the System.Guid class
					// is three times as slow, and produces bad-looking hex strings to boot
					random1.NextBytes(byteArray1);
					charArray1[0] = HEX_CHARS[byteArray1[0] & 15];
					charArray1[1] = HEX_CHARS[byteArray1[0] >> 4];
					charArray1[2] = HEX_CHARS[byteArray1[1] & 15];
					charArray1[3] = HEX_CHARS[byteArray1[1] >> 4];
					charArray1[4] = HEX_CHARS[byteArray1[2] & 15];
					charArray1[5] = HEX_CHARS[byteArray1[2] >> 4];
					charArray1[6] = HEX_CHARS[byteArray1[3] & 15];
					charArray1[7] = HEX_CHARS[byteArray1[3] >> 4];
					charArray1[8] = HEX_CHARS[byteArray1[4] & 15];
					charArray1[9] = HEX_CHARS[byteArray1[4] >> 4];
					charArray1[10] = HEX_CHARS[byteArray1[5] & 15];
					charArray1[11] = HEX_CHARS[byteArray1[5] >> 4];
					charArray1[12] = HEX_CHARS[byteArray1[6] & 15];
					charArray1[13] = HEX_CHARS[byteArray1[6] >> 4];
					charArray1[14] = HEX_CHARS[byteArray1[7] & 15];
					charArray1[15] = HEX_CHARS[byteArray1[7] >> 4];
					charArray1[16] = HEX_CHARS[byteArray1[8] & 15];
					charArray1[17] = HEX_CHARS[byteArray1[8] >> 4];
					charArray1[18] = HEX_CHARS[byteArray1[9] & 15];
					charArray1[19] = HEX_CHARS[byteArray1[9] >> 4];
					charArray1[20] = HEX_CHARS[byteArray1[10] & 15];
					charArray1[21] = HEX_CHARS[byteArray1[10] >> 4];
					charArray1[22] = HEX_CHARS[byteArray1[11] & 15];
					charArray1[23] = HEX_CHARS[byteArray1[11] >> 4];
					charArray1[24] = HEX_CHARS[byteArray1[12] & 15];
					charArray1[25] = HEX_CHARS[byteArray1[12] >> 4];
					charArray1[26] = HEX_CHARS[byteArray1[13] & 15];
					charArray1[27] = HEX_CHARS[byteArray1[13] >> 4];
					charArray1[28] = HEX_CHARS[byteArray1[14] & 15];
					charArray1[29] = HEX_CHARS[byteArray1[14] >> 4];
					charArray1[30] = HEX_CHARS[byteArray1[15] & 15];
					charArray1[31] = HEX_CHARS[byteArray1[15] >> 4];
					_id = new string(charArray1);
				}
				Event ev = new Event(new DateTime(ticks), defaultEventLevel, _message, _data, null, null, _id);
				if (filter != null)
				{
					Filter f = filter;
					Monitor.Exit(monitor);
					try
					{
						if (!filter.Accepts(ev))
						{
							return;
						}
					}
					catch { }
					Monitor.Enter(monitor);
					if (isClosed)
					{
						return;
					}
				}
				try
				{
					Write(ev);
				}
				catch (Exception e)
				{
					exceptionHandler.Handle(new LoggerException(this, ev, e));
				}
				if (childCount > 0)
				{
					Monitor.Enter(childMonitor);
					//                    lock (childMonitor) {
					Monitor.Exit(monitor);
					for (int x = 0; x < childCount; x++)
					{
						children[x].LogEvent(defaultEventLevel, _message, _data, null, null, _id);
					}
					Monitor.Exit(childMonitor);
					//                    }
				}
				else
				{
					Monitor.Exit(monitor);
				}
			}
			//        }
		}

		/// <summary>
		/// Creates an Event with the specified string message and object payload
		/// </summary>
		/// <param name="_message">The string message to log</param>
		/// <param name="_data">The data payload</param>
		/// <param name="_category">The category of the application event</param>
		public void Log(string _message, object _data, string _category)
		{
			if ((_message == null) && (_data == null))
			{
				return;
			}
			LogEvent(defaultEventLevel, _message, _data, _category, null, null);
		}

		/// <summary>
		/// Creates an Event with the specified string message and object payload
		/// </summary>
		/// <param name="_level">The Level to apply to the created Event</param>
		/// <param name="_message">The string message to log</param>
		/// <param name="_data">The data payload</param>
		public void Log(Level _level, string _message, object _data)
		{
			if ((_message == null) && (_data == null))
			{
				return;
			}
			if (_level == null)
			{
				_level = defaultEventLevel;
			}
			if (_level.Priority < levelPriority)
			{
				return;
			}
			Context context;
			if (isContextEnabled)
			{
				context = Context.CurrentContext.Copy();
			}
			else
			{
				context = null;
			}
			long ticks = DateTime.Now.Ticks;
			Monitor.Enter(monitor);
			//        lock (monitor) {
			if (isClosed)
			{
				Monitor.Exit(monitor);
				throw new ApplicationException("This logger is closed");
			}
			if (isBufferingEnabled)
			{
				if (bufferCount < bufferSize)
				{
					contextBuffer[bufferCount] = context;
					levelBuffer[bufferCount] = _level;
					timestampBuffer[bufferCount] = ticks;
					messageBuffer[bufferCount] = _message;
					dataBuffer[bufferCount] = _data;
					bufferCount++;
					Monitor.Exit(monitor);
				}
				else
				{
					if (bufferCount >= maximumBufferSize)
					{
						Monitor.Exit(monitor);
						return;
					}
					else if (bufferCount == contextBuffer.Length)
					{
						int newSize = contextBuffer.Length * 2;

						Context[] newContextBuffer = new Context[newSize];
						Level[] newLevelBuffer = new Level[newSize];
						long[] newTimestampBuffer = new long[newSize];
						string[] newMessageBuffer = new string[newSize];
						object[] newDataBuffer = new object[newSize];
						string[] newCategoryBuffer = new string[newSize];
						string[] newCodeBuffer = new string[newSize];
						string[] newIDBuffer = new string[newSize];

						Array.Copy(contextBuffer, 0, newContextBuffer, 0, bufferCount);
						Array.Copy(levelBuffer, 0, newLevelBuffer, 0, bufferCount);
						Array.Copy(timestampBuffer, 0, newTimestampBuffer, 0, bufferCount);
						Array.Copy(messageBuffer, 0, newMessageBuffer, 0, bufferCount);
						Array.Copy(dataBuffer, 0, newDataBuffer, 0, bufferCount);
						Array.Copy(categoryBuffer, 0, newCategoryBuffer, 0, bufferCount);
						Array.Copy(codeBuffer, 0, newCodeBuffer, 0, bufferCount);
						Array.Copy(idBuffer, 0, newIDBuffer, 0, bufferCount);

						contextBuffer = newContextBuffer;
						levelBuffer = newLevelBuffer;
						timestampBuffer = newTimestampBuffer;
						messageBuffer = newMessageBuffer;
						dataBuffer = newDataBuffer;
						categoryBuffer = newCategoryBuffer;
						codeBuffer = newCodeBuffer;
						idBuffer = newIDBuffer;

						contextBuffer[bufferCount] = context;
						levelBuffer[bufferCount] = _level;
						timestampBuffer[bufferCount] = ticks;
						messageBuffer[bufferCount] = _message;
						dataBuffer[bufferCount] = _data;
						bufferCount++;
						if (bufferCount == bufferSize)
						{
							flushingThread.Awaken();
						}
						Monitor.Exit(monitor);
					}
					else
					{
						contextBuffer[bufferCount] = context;
						levelBuffer[bufferCount] = _level;
						timestampBuffer[bufferCount] = ticks;
						messageBuffer[bufferCount] = _message;
						dataBuffer[bufferCount] = _data;
						bufferCount++;
						if (bufferCount == bufferSize)
						{
							flushingThread.Awaken();
						}
						Monitor.Exit(monitor);
					}
				}
			}
			else
			{
				string _id = null;
				if (isIDGenerationEnabled)
				{
					// This may look dorky, but using the System.Guid class
					// is three times as slow, and produces bad-looking hex strings to boot
					random1.NextBytes(byteArray1);
					charArray1[0] = HEX_CHARS[byteArray1[0] & 15];
					charArray1[1] = HEX_CHARS[byteArray1[0] >> 4];
					charArray1[2] = HEX_CHARS[byteArray1[1] & 15];
					charArray1[3] = HEX_CHARS[byteArray1[1] >> 4];
					charArray1[4] = HEX_CHARS[byteArray1[2] & 15];
					charArray1[5] = HEX_CHARS[byteArray1[2] >> 4];
					charArray1[6] = HEX_CHARS[byteArray1[3] & 15];
					charArray1[7] = HEX_CHARS[byteArray1[3] >> 4];
					charArray1[8] = HEX_CHARS[byteArray1[4] & 15];
					charArray1[9] = HEX_CHARS[byteArray1[4] >> 4];
					charArray1[10] = HEX_CHARS[byteArray1[5] & 15];
					charArray1[11] = HEX_CHARS[byteArray1[5] >> 4];
					charArray1[12] = HEX_CHARS[byteArray1[6] & 15];
					charArray1[13] = HEX_CHARS[byteArray1[6] >> 4];
					charArray1[14] = HEX_CHARS[byteArray1[7] & 15];
					charArray1[15] = HEX_CHARS[byteArray1[7] >> 4];
					charArray1[16] = HEX_CHARS[byteArray1[8] & 15];
					charArray1[17] = HEX_CHARS[byteArray1[8] >> 4];
					charArray1[18] = HEX_CHARS[byteArray1[9] & 15];
					charArray1[19] = HEX_CHARS[byteArray1[9] >> 4];
					charArray1[20] = HEX_CHARS[byteArray1[10] & 15];
					charArray1[21] = HEX_CHARS[byteArray1[10] >> 4];
					charArray1[22] = HEX_CHARS[byteArray1[11] & 15];
					charArray1[23] = HEX_CHARS[byteArray1[11] >> 4];
					charArray1[24] = HEX_CHARS[byteArray1[12] & 15];
					charArray1[25] = HEX_CHARS[byteArray1[12] >> 4];
					charArray1[26] = HEX_CHARS[byteArray1[13] & 15];
					charArray1[27] = HEX_CHARS[byteArray1[13] >> 4];
					charArray1[28] = HEX_CHARS[byteArray1[14] & 15];
					charArray1[29] = HEX_CHARS[byteArray1[14] >> 4];
					charArray1[30] = HEX_CHARS[byteArray1[15] & 15];
					charArray1[31] = HEX_CHARS[byteArray1[15] >> 4];
					_id = new string(charArray1);
				}
				Event ev = new Event(new DateTime(ticks), _level, _message, _data, null, null, _id);
				if (filter != null)
				{
					Filter f = filter;
					Monitor.Exit(monitor);
					try
					{
						if (!filter.Accepts(ev))
						{
							return;
						}
					}
					catch { }
					Monitor.Enter(monitor);
					if (isClosed)
					{
						return;
					}
				}
				try
				{
					Write(ev);
				}
				catch (Exception e)
				{
					exceptionHandler.Handle(new LoggerException(this, ev, e));
				}
				if (childCount > 0)
				{
					Monitor.Enter(childMonitor);
					//                    lock (childMonitor) {
					Monitor.Exit(monitor);
					for (int x = 0; x < childCount; x++)
					{
						children[x].LogEvent(_level, _message, _data, null, null, _id);
					}
					Monitor.Exit(childMonitor);
					//                    }
				}
				else
				{
					Monitor.Exit(monitor);
				}
			}
			//        }
		}

		/// <summary>
		/// Creates an Event with the specified string message and object payload
		/// </summary>
		/// <param name="_level">The Level to apply to the created Event</param>
		/// <param name="_message">The string message to log</param>
		/// <param name="_data">The data payload</param>
		/// <param name="_category">The category of the application event</param>
		public void Log(Level _level, string _message, object _data, string _category)
		{
			if ((_message == null) && (_data == null))
			{
				return;
			}
			LogEvent(((_level == null) ? defaultEventLevel : _level), _message, _data, _category, null, null);
		}

		/// <summary>
		/// Creates an Event with the specified string message, object payload, event category and event code
		/// </summary>
		/// <param name="_message">The string message to log</param>
		/// <param name="_data">The data payload</param>
		/// <param name="_category">The category of the application event (can be null)</param>
		/// <param name="_code">The code of the application event (can be null)</param>
		public void Log(string _message, object _data, string _category, string _code)
		{
			if ((_message == null) && (_data == null))
			{
				return;
			}
			LogEvent(defaultEventLevel, _message, _data, _category, _code, null);
		}

		/// <summary>
		/// Creates an Event with the specified string message, object payload, event category and event code
		/// </summary>
		/// <param name="_message">The string message to log</param>
		/// <param name="_data">The data payload</param>
		/// <param name="_category">The category of the application event (can be null)</param>
		/// <param name="_code">The code of the application event (can be null)</param>
		/// <param name="_id">The ID of the application event (can be null)</param>
		public void Log(string _message, object _data, string _category, string _code, string _id)
		{
			if ((_message == null) && (_data == null))
			{
				return;
			}
			LogEvent(defaultEventLevel, _message, _data, _category, _code, _id);
		}

		/// <summary>
		/// Creates an Event object with the specified level, string message, object payload, event category and event code
		/// </summary>
		/// <param name="_level">The Level to apply to the created Event</param>
		/// <param name="_message">The string message to log</param>
		/// <param name="_data">The data payload</param>
		/// <param name="_category">The category of the application event (can be null)</param>
		/// <param name="_code">The code of the application event (can be null)</param>
		public void Log(Level _level, string _message, object _data, string _category, string _code)
		{
			if ((_message == null) && (_data == null))
			{
				return;
			}
			LogEvent(((_level == null) ? defaultEventLevel : _level), _message, _data, _category, _code, null);
		}

		/// <summary>
		/// Creates an Event object with the specified level, string message, object payload, event category and event code
		/// </summary>
		/// <param name="_level">The Level to apply to the created Event</param>
		/// <param name="_message">The string message to log</param>
		/// <param name="_data">The data payload</param>
		/// <param name="_category">The category of the application event (can be null)</param>
		/// <param name="_code">The code of the application event (can be null)</param>
		/// <param name="_id">The ID of the application event (can be null)</param>
		public void Log(Level _level, string _message, object _data, string _category, string _code, string _id)
		{
			if ((_message == null) && (_data == null))
			{
				return;
			}
			LogEvent(((_level == null) ? defaultEventLevel : _level), _message, _data, _category, _code, _id);
		}

		/// <summary>
		/// Creates an Event with the specified data object, the logger's default Level, and its ApplicationName and SystemName properties
		/// </summary>
		/// <param name="_data">The data to log</param>
		public void Log(object _data)
		{
			if (_data == null)
			{
				return;
			}
			if (defaultEventLevel.Priority < levelPriority)
			{
				return;
			}
			Context context;
			if (isContextEnabled)
			{
				context = Context.CurrentContext.Copy();
			}
			else
			{
				context = null;
			}
			long ticks = DateTime.Now.Ticks;
			Monitor.Enter(monitor);
			//        lock (monitor) {
			if (isClosed)
			{
				Monitor.Exit(monitor);
				throw new ApplicationException("This logger is closed");
			}
			if (isBufferingEnabled)
			{
				if (bufferCount < bufferSize)
				{
					contextBuffer[bufferCount] = context;
					levelBuffer[bufferCount] = defaultEventLevel;
					timestampBuffer[bufferCount] = ticks;
					dataBuffer[bufferCount] = _data;
					bufferCount++;
					Monitor.Exit(monitor);
				}
				else
				{
					if (bufferCount >= maximumBufferSize)
					{
						Monitor.Exit(monitor);
						return;
					}
					else if (bufferCount == contextBuffer.Length)
					{
						int newSize = contextBuffer.Length * 2;

						Context[] newContextBuffer = new Context[newSize];
						Level[] newLevelBuffer = new Level[newSize];
						long[] newTimestampBuffer = new long[newSize];
						string[] newMessageBuffer = new string[newSize];
						object[] newDataBuffer = new object[newSize];
						string[] newCategoryBuffer = new string[newSize];
						string[] newCodeBuffer = new string[newSize];
						string[] newIDBuffer = new string[newSize];

						Array.Copy(contextBuffer, 0, newContextBuffer, 0, bufferCount);
						Array.Copy(levelBuffer, 0, newLevelBuffer, 0, bufferCount);
						Array.Copy(timestampBuffer, 0, newTimestampBuffer, 0, bufferCount);
						Array.Copy(messageBuffer, 0, newMessageBuffer, 0, bufferCount);
						Array.Copy(dataBuffer, 0, newDataBuffer, 0, bufferCount);
						Array.Copy(categoryBuffer, 0, newCategoryBuffer, 0, bufferCount);
						Array.Copy(codeBuffer, 0, newCodeBuffer, 0, bufferCount);
						Array.Copy(idBuffer, 0, newIDBuffer, 0, bufferCount);

						contextBuffer = newContextBuffer;
						levelBuffer = newLevelBuffer;
						timestampBuffer = newTimestampBuffer;
						messageBuffer = newMessageBuffer;
						dataBuffer = newDataBuffer;
						categoryBuffer = newCategoryBuffer;
						codeBuffer = newCodeBuffer;
						idBuffer = newIDBuffer;

						contextBuffer[bufferCount] = context;
						levelBuffer[bufferCount] = defaultEventLevel;
						timestampBuffer[bufferCount] = ticks;
						dataBuffer[bufferCount] = _data;
						bufferCount++;
						if (bufferCount == bufferSize)
						{
							flushingThread.Awaken();
						}
						Monitor.Exit(monitor);
					}
					else
					{
						contextBuffer[bufferCount] = context;
						levelBuffer[bufferCount] = defaultEventLevel;
						timestampBuffer[bufferCount] = ticks;
						dataBuffer[bufferCount] = _data;
						bufferCount++;
						if (bufferCount == bufferSize)
						{
							flushingThread.Awaken();
						}
						Monitor.Exit(monitor);
					}
				}
			}
			else
			{
				string _id = null;
				if (isIDGenerationEnabled)
				{
					// This may look dorky, but using the System.Guid class
					// is three times as slow, and produces bad-looking hex strings to boot
					random1.NextBytes(byteArray1);
					charArray1[0] = HEX_CHARS[byteArray1[0] & 15];
					charArray1[1] = HEX_CHARS[byteArray1[0] >> 4];
					charArray1[2] = HEX_CHARS[byteArray1[1] & 15];
					charArray1[3] = HEX_CHARS[byteArray1[1] >> 4];
					charArray1[4] = HEX_CHARS[byteArray1[2] & 15];
					charArray1[5] = HEX_CHARS[byteArray1[2] >> 4];
					charArray1[6] = HEX_CHARS[byteArray1[3] & 15];
					charArray1[7] = HEX_CHARS[byteArray1[3] >> 4];
					charArray1[8] = HEX_CHARS[byteArray1[4] & 15];
					charArray1[9] = HEX_CHARS[byteArray1[4] >> 4];
					charArray1[10] = HEX_CHARS[byteArray1[5] & 15];
					charArray1[11] = HEX_CHARS[byteArray1[5] >> 4];
					charArray1[12] = HEX_CHARS[byteArray1[6] & 15];
					charArray1[13] = HEX_CHARS[byteArray1[6] >> 4];
					charArray1[14] = HEX_CHARS[byteArray1[7] & 15];
					charArray1[15] = HEX_CHARS[byteArray1[7] >> 4];
					charArray1[16] = HEX_CHARS[byteArray1[8] & 15];
					charArray1[17] = HEX_CHARS[byteArray1[8] >> 4];
					charArray1[18] = HEX_CHARS[byteArray1[9] & 15];
					charArray1[19] = HEX_CHARS[byteArray1[9] >> 4];
					charArray1[20] = HEX_CHARS[byteArray1[10] & 15];
					charArray1[21] = HEX_CHARS[byteArray1[10] >> 4];
					charArray1[22] = HEX_CHARS[byteArray1[11] & 15];
					charArray1[23] = HEX_CHARS[byteArray1[11] >> 4];
					charArray1[24] = HEX_CHARS[byteArray1[12] & 15];
					charArray1[25] = HEX_CHARS[byteArray1[12] >> 4];
					charArray1[26] = HEX_CHARS[byteArray1[13] & 15];
					charArray1[27] = HEX_CHARS[byteArray1[13] >> 4];
					charArray1[28] = HEX_CHARS[byteArray1[14] & 15];
					charArray1[29] = HEX_CHARS[byteArray1[14] >> 4];
					charArray1[30] = HEX_CHARS[byteArray1[15] & 15];
					charArray1[31] = HEX_CHARS[byteArray1[15] >> 4];
					_id = new string(charArray1);
				}
				Event ev = new Event(new DateTime(ticks), defaultEventLevel, null, _data, null, null, _id);
				if (filter != null)
				{
					Filter f = filter;
					Monitor.Exit(monitor);
					try
					{
						if (!filter.Accepts(ev))
						{
							return;
						}
					}
					catch { }
					Monitor.Enter(monitor);
					if (isClosed)
					{
						return;
					}
				}
				try
				{
					Write(ev);
				}
				catch (Exception e)
				{
					exceptionHandler.Handle(new LoggerException(this, ev, e));
				}
				if (childCount > 0)
				{
					Monitor.Enter(childMonitor);
					//                    lock (childMonitor) {
					Monitor.Exit(monitor);
					for (int x = 0; x < childCount; x++)
					{
						children[x].LogEvent(defaultEventLevel, null, _data, null, null, _id);
					}
					Monitor.Exit(childMonitor);
					//                    }
				}
				else
				{
					Monitor.Exit(monitor);
				}
			}
			//        }
		}

		/// <summary>
		/// Creates an Event with the specified data object, the logger's default Level, and the specified category
		/// </summary>
		/// <param name="_data">The data to log</param>
		/// <param name="_category">The category of the application event</param>
		public void Log(object _data, string _category)
		{
			if (_data == null)
			{
				return;
			}
			LogEvent(defaultEventLevel, null, _data, _category, null, null);
		}

		/// <summary>
		/// Creates an Event with the specified level, the specified data object, and the logger's ApplicationName and SystemName properties
		/// </summary>
		/// <param name="_level">The Level to apply to the created Event</param>
		/// <param name="_data">The data to log</param>
		public void Log(Level _level, object _data)
		{
			if (_data == null)
			{
				return;
			}
			if (_level == null)
			{
				_level = defaultEventLevel;
			}
			if (_level.Priority < levelPriority)
			{
				return;
			}
			Context context;
			if (isContextEnabled)
			{
				context = Context.CurrentContext.Copy();
			}
			else
			{
				context = null;
			}
			long ticks = DateTime.Now.Ticks;
			Monitor.Enter(monitor);
			//        lock (monitor) {
			if (isClosed)
			{
				Monitor.Exit(monitor);
				throw new ApplicationException("This logger is closed");
			}
			if (isBufferingEnabled)
			{
				if (bufferCount < bufferSize)
				{
					contextBuffer[bufferCount] = context;
					levelBuffer[bufferCount] = _level;
					timestampBuffer[bufferCount] = ticks;
					dataBuffer[bufferCount] = _data;
					bufferCount++;
					Monitor.Exit(monitor);
				}
				else
				{
					if (bufferCount >= maximumBufferSize)
					{
						Monitor.Exit(monitor);
						return;
					}
					else if (bufferCount == contextBuffer.Length)
					{
						int newSize = contextBuffer.Length * 2;

						Context[] newContextBuffer = new Context[newSize];
						Level[] newLevelBuffer = new Level[newSize];
						long[] newTimestampBuffer = new long[newSize];
						string[] newMessageBuffer = new string[newSize];
						object[] newDataBuffer = new object[newSize];
						string[] newCategoryBuffer = new string[newSize];
						string[] newCodeBuffer = new string[newSize];
						string[] newIDBuffer = new string[newSize];

						Array.Copy(contextBuffer, 0, newContextBuffer, 0, bufferCount);
						Array.Copy(levelBuffer, 0, newLevelBuffer, 0, bufferCount);
						Array.Copy(timestampBuffer, 0, newTimestampBuffer, 0, bufferCount);
						Array.Copy(messageBuffer, 0, newMessageBuffer, 0, bufferCount);
						Array.Copy(dataBuffer, 0, newDataBuffer, 0, bufferCount);
						Array.Copy(categoryBuffer, 0, newCategoryBuffer, 0, bufferCount);
						Array.Copy(codeBuffer, 0, newCodeBuffer, 0, bufferCount);
						Array.Copy(idBuffer, 0, newIDBuffer, 0, bufferCount);

						contextBuffer = newContextBuffer;
						levelBuffer = newLevelBuffer;
						timestampBuffer = newTimestampBuffer;
						messageBuffer = newMessageBuffer;
						dataBuffer = newDataBuffer;
						categoryBuffer = newCategoryBuffer;
						codeBuffer = newCodeBuffer;
						idBuffer = newIDBuffer;

						contextBuffer[bufferCount] = context;
						levelBuffer[bufferCount] = _level;
						timestampBuffer[bufferCount] = ticks;
						dataBuffer[bufferCount] = _data;
						bufferCount++;
						if (bufferCount == bufferSize)
						{
							flushingThread.Awaken();
						}
						Monitor.Exit(monitor);
					}
					else
					{
						contextBuffer[bufferCount] = context;
						levelBuffer[bufferCount] = _level;
						timestampBuffer[bufferCount] = ticks;
						dataBuffer[bufferCount] = _data;
						bufferCount++;
						if (bufferCount == bufferSize)
						{
							flushingThread.Awaken();
						}
						Monitor.Exit(monitor);
					}
				}
			}
			else
			{
				string _id = null;
				if (isIDGenerationEnabled)
				{
					// This may look dorky, but using the System.Guid class
					// is three times as slow, and produces bad-looking hex strings to boot
					random1.NextBytes(byteArray1);
					charArray1[0] = HEX_CHARS[byteArray1[0] & 15];
					charArray1[1] = HEX_CHARS[byteArray1[0] >> 4];
					charArray1[2] = HEX_CHARS[byteArray1[1] & 15];
					charArray1[3] = HEX_CHARS[byteArray1[1] >> 4];
					charArray1[4] = HEX_CHARS[byteArray1[2] & 15];
					charArray1[5] = HEX_CHARS[byteArray1[2] >> 4];
					charArray1[6] = HEX_CHARS[byteArray1[3] & 15];
					charArray1[7] = HEX_CHARS[byteArray1[3] >> 4];
					charArray1[8] = HEX_CHARS[byteArray1[4] & 15];
					charArray1[9] = HEX_CHARS[byteArray1[4] >> 4];
					charArray1[10] = HEX_CHARS[byteArray1[5] & 15];
					charArray1[11] = HEX_CHARS[byteArray1[5] >> 4];
					charArray1[12] = HEX_CHARS[byteArray1[6] & 15];
					charArray1[13] = HEX_CHARS[byteArray1[6] >> 4];
					charArray1[14] = HEX_CHARS[byteArray1[7] & 15];
					charArray1[15] = HEX_CHARS[byteArray1[7] >> 4];
					charArray1[16] = HEX_CHARS[byteArray1[8] & 15];
					charArray1[17] = HEX_CHARS[byteArray1[8] >> 4];
					charArray1[18] = HEX_CHARS[byteArray1[9] & 15];
					charArray1[19] = HEX_CHARS[byteArray1[9] >> 4];
					charArray1[20] = HEX_CHARS[byteArray1[10] & 15];
					charArray1[21] = HEX_CHARS[byteArray1[10] >> 4];
					charArray1[22] = HEX_CHARS[byteArray1[11] & 15];
					charArray1[23] = HEX_CHARS[byteArray1[11] >> 4];
					charArray1[24] = HEX_CHARS[byteArray1[12] & 15];
					charArray1[25] = HEX_CHARS[byteArray1[12] >> 4];
					charArray1[26] = HEX_CHARS[byteArray1[13] & 15];
					charArray1[27] = HEX_CHARS[byteArray1[13] >> 4];
					charArray1[28] = HEX_CHARS[byteArray1[14] & 15];
					charArray1[29] = HEX_CHARS[byteArray1[14] >> 4];
					charArray1[30] = HEX_CHARS[byteArray1[15] & 15];
					charArray1[31] = HEX_CHARS[byteArray1[15] >> 4];
					_id = new string(charArray1);
				}
				Event ev = new Event(new DateTime(ticks), _level, null, _data, null, null, _id);
				if (filter != null)
				{
					Filter f = filter;
					Monitor.Exit(monitor);
					try
					{
						if (!filter.Accepts(ev))
						{
							return;
						}
					}
					catch { }
					Monitor.Enter(monitor);
					if (isClosed)
					{
						return;
					}
				}
				try
				{
					Write(ev);
				}
				catch (Exception e)
				{
					exceptionHandler.Handle(new LoggerException(this, ev, e));
				}
				if (childCount > 0)
				{
					Monitor.Enter(childMonitor);
					//                    lock (childMonitor) {
					Monitor.Exit(monitor);
					for (int x = 0; x < childCount; x++)
					{
						children[x].LogEvent(_level, null, _data, null, null, _id);
					}
					Monitor.Exit(childMonitor);
					//                    }
				}
				else
				{
					Monitor.Exit(monitor);
				}
			}
			//        }
		}

		/// <summary>
		/// Creates an Event with the specified level, the specified data object, and the logger's ApplicationName and SystemName properties
		/// </summary>
		/// <param name="_level">The Level to apply to the created Event</param>
		/// <param name="_data">The data to log</param>
		/// <param name="_category">The category of the application event</param>
		public void Log(Level _level, object _data, string _category)
		{
			if (_data == null)
			{
				return;
			}
			LogEvent(((_level == null) ? defaultEventLevel : _level), null, _data, _category, null, null);
		}

		/// <summary>
		/// Creates an Event using the specified data object, event category, and event code
		/// </summary>
		/// <param name="_data">The data payload</param>
		/// <param name="_category">The application event category (can be null)</param>
		/// <param name="_code">The application event code (can be null)</param>
		public void Log(object _data, string _category, string _code)
		{
			if (_data == null)
			{
				return;
			}
			LogEvent(defaultEventLevel, null, _data, _category, _code, null);
		}

		/// <summary>
		/// Creates an Event using the specified data object, event category, and event code
		/// </summary>
		/// <param name="_data">The data payload</param>
		/// <param name="_category">The application event category (can be null)</param>
		/// <param name="_code">The application event code (can be null)</param>
		/// <param name="_id">The application event ID (can be null)</param>
		public void Log(object _data, string _category, string _code, string _id)
		{
			if (_data == null)
			{
				return;
			}
			LogEvent(defaultEventLevel, null, _data, _category, _code, _id);
		}

		/// <summary>
		/// Creates an Event using the specified level, data object, event category, and event code
		/// </summary>
		/// <param name="_level">The Level to apply to the created Event</param>
		/// <param name="_data">The data payload</param>
		/// <param name="_category">The category of the application event</param>
		/// <param name="_code">The application event code</param>
		public void Log(Level _level, object _data, string _category, string _code)
		{
			if (_data == null)
			{
				return;
			}
			LogEvent(((_level == null) ? defaultEventLevel : _level), null, _data, _category, _code, null);
		}

		/// <summary>
		/// Creates an Event using the specified level, data object, event category, and event code
		/// </summary>
		/// <param name="_level">The Level to apply to the created Event</param>
		/// <param name="_data">The data payload</param>
		/// <param name="_category">The category of the application event</param>
		/// <param name="_code">The application event code</param>
		/// <param name="_id">The application event ID</param>
		public void Log(Level _level, object _data, string _category, string _code, string _id)
		{
			if (_data == null)
			{
				return;
			}
			LogEvent(((_level == null) ? defaultEventLevel : _level), null, _data, _category, _code, _id);
		}

		/// <summary>
		/// Logs an exception, creating an Event with level Level.Exception and a message 
		/// containing the complete stack trace.  If IsEventAutoFlushEnabled is true,
		/// the contents of the buffer are immediately flushed in buffering mode.
		/// </summary>
		/// <param name="_exception">The exception to log</param>
		public void Log(Exception _exception)
		{
			if (_exception == null)
			{
				return;
			}
			if (isExceptionAutoFlushEnabled)
			{
				lock (monitor)
				{
					LogEvent(Level.Exception, _exception.ToString(), null, null, null, null);
					if ((isBufferingEnabled) && (bufferCount < bufferSize))
					{
						try
						{
							flushingThread.Awaken();
						}
						catch { }
					}
				}
			}
			else
			{
				LogEvent(Level.Exception, _exception.ToString(), null, null, null, null);
			}
		}

		/// <summary>
		/// Logs an exception, creating an Event with level Level.Exception and a message 
		/// containing the complete stack trace
		/// </summary>
		/// <param name="_exception">The exception to log</param>
		/// <param name="_id">The event ID</param>
		public void Log(Exception _exception, string _id)
		{
			if (_exception == null)
			{
				return;
			}
			if (isExceptionAutoFlushEnabled)
			{
				lock (monitor)
				{
					LogEvent(Level.Exception, _exception.ToString(), null, null, null, _id);
					if ((isBufferingEnabled) && (bufferCount < bufferSize))
					{
						try
						{
							flushingThread.Awaken();
						}
						catch { }
					}
				}
			}
			else
			{
				LogEvent(Level.Exception, _exception.ToString(), null, null, null, _id);
			}
		}

		/// <summary>
		/// Implements the ExceptionHandler interface,
		/// providing the ability for a logger to act as another
		/// logger's exception handler.  This results in the logging of
		/// the event(s) not successfully logged by the other logger.  If
		/// this fails, generating a LoggerException, this logger's exception handler
		/// is invoked
		/// </summary>
		/// <param name="e">The exception to handle</param>
		public void Handle(LoggerException e)
		{
			if (e == null)
			{
				return;
			}
			if (e.IsBatchException)
			{
				Event ev;
				for (int x = 0; x < e.Exceptions.Length; x++)
				{
					ev = e.Event;
					if (ev != null)
					{
						try
						{
							lock (monitor)
							{
								LogEvent(ev.Level, ev.Message, ev.Data, ev.Category, ev.Code, ev.ID);
							}
						}
						catch (LoggerException exception)
						{
							try
							{
								exceptionHandler.Handle(exception);
							}
							catch
							{
								// For now, nothing will be done.  What's appropriate to do?
								// Write something out to the screen?  Maybe that's not desirable.
							}
						}
					}
				}
			}
			else
			{
				Event ev = e.Event;
				try
				{
					LogEvent(ev.Level, ev.Message, ev.Data, ev.Category, ev.Code, ev.ID);
				}
				catch (LoggerException exception)
				{
					try
					{
						exceptionHandler.Handle(exception);
					}
					catch { }
				}
			}
		}

		// Level level, string message, object data, string category, string code, string id
		private void LogEvent(Level _level, string _message, object _data, string _category, string _code, string _id)
		{
			if (_level.Priority < levelPriority)
			{
				return;
			}
			Context context;
			if (isContextEnabled)
			{
				context = Context.CurrentContext.Copy();
			}
			else
			{
				context = null;
			}
			long ticks = DateTime.Now.Ticks;
			Monitor.Enter(monitor);
			//        lock (monitor) {
			if (isClosed)
			{
				Monitor.Exit(monitor);
				throw new ApplicationException("This logger is closed");
			}
			if (isBufferingEnabled)
			{
				if (bufferCount < bufferSize)
				{
					contextBuffer[bufferCount] = context;
					levelBuffer[bufferCount] = _level;
					timestampBuffer[bufferCount] = ticks;
					messageBuffer[bufferCount] = _message;
					dataBuffer[bufferCount] = _data;
					categoryBuffer[bufferCount] = _category;
					codeBuffer[bufferCount] = _code;
					idBuffer[bufferCount] = _id;
					bufferCount++;
					Monitor.Exit(monitor);
				}
				else
				{
					if (bufferCount >= maximumBufferSize)
					{
						Monitor.Exit(monitor);
						return;
					}
					else if (bufferCount == contextBuffer.Length)
					{
						int newSize = contextBuffer.Length * 2;

						Context[] newContextBuffer = new Context[newSize];
						Level[] newLevelBuffer = new Level[newSize];
						long[] newTimestampBuffer = new long[newSize];
						string[] newMessageBuffer = new string[newSize];
						object[] newDataBuffer = new object[newSize];
						string[] newCategoryBuffer = new string[newSize];
						string[] newCodeBuffer = new string[newSize];
						string[] newIDBuffer = new string[newSize];

						Array.Copy(contextBuffer, 0, newContextBuffer, 0, bufferCount);
						Array.Copy(levelBuffer, 0, newLevelBuffer, 0, bufferCount);
						Array.Copy(timestampBuffer, 0, newTimestampBuffer, 0, bufferCount);
						Array.Copy(messageBuffer, 0, newMessageBuffer, 0, bufferCount);
						Array.Copy(dataBuffer, 0, newDataBuffer, 0, bufferCount);
						Array.Copy(categoryBuffer, 0, newCategoryBuffer, 0, bufferCount);
						Array.Copy(codeBuffer, 0, newCodeBuffer, 0, bufferCount);
						Array.Copy(idBuffer, 0, newIDBuffer, 0, bufferCount);

						contextBuffer = newContextBuffer;
						levelBuffer = newLevelBuffer;
						timestampBuffer = newTimestampBuffer;
						messageBuffer = newMessageBuffer;
						dataBuffer = newDataBuffer;
						categoryBuffer = newCategoryBuffer;
						codeBuffer = newCodeBuffer;
						idBuffer = newIDBuffer;

						contextBuffer[bufferCount] = context;
						levelBuffer[bufferCount] = _level;
						timestampBuffer[bufferCount] = ticks;
						messageBuffer[bufferCount] = _message;
						dataBuffer[bufferCount] = _data;
						categoryBuffer[bufferCount] = _category;
						codeBuffer[bufferCount] = _code;
						idBuffer[bufferCount] = _id;
						bufferCount++;
						if (bufferCount == bufferSize)
						{
							flushingThread.Awaken();
						}
						Monitor.Exit(monitor);
					}
					else
					{
						contextBuffer[bufferCount] = context;
						levelBuffer[bufferCount] = _level;
						timestampBuffer[bufferCount] = ticks;
						messageBuffer[bufferCount] = _message;
						dataBuffer[bufferCount] = _data;
						categoryBuffer[bufferCount] = _category;
						codeBuffer[bufferCount] = _code;
						idBuffer[bufferCount] = _id;
						bufferCount++;
						if (bufferCount == bufferSize)
						{
							flushingThread.Awaken();
						}
						Monitor.Exit(monitor);
					}
				}
			}
			else
			{
				if (isIDGenerationEnabled && (_id == null))
				{
					// This may look dorky, but using the System.Guid class
					// is three times as slow, and produces bad-looking hex strings to boot
					random1.NextBytes(byteArray1);
					charArray1[0] = HEX_CHARS[byteArray1[0] & 15];
					charArray1[1] = HEX_CHARS[byteArray1[0] >> 4];
					charArray1[2] = HEX_CHARS[byteArray1[1] & 15];
					charArray1[3] = HEX_CHARS[byteArray1[1] >> 4];
					charArray1[4] = HEX_CHARS[byteArray1[2] & 15];
					charArray1[5] = HEX_CHARS[byteArray1[2] >> 4];
					charArray1[6] = HEX_CHARS[byteArray1[3] & 15];
					charArray1[7] = HEX_CHARS[byteArray1[3] >> 4];
					charArray1[8] = HEX_CHARS[byteArray1[4] & 15];
					charArray1[9] = HEX_CHARS[byteArray1[4] >> 4];
					charArray1[10] = HEX_CHARS[byteArray1[5] & 15];
					charArray1[11] = HEX_CHARS[byteArray1[5] >> 4];
					charArray1[12] = HEX_CHARS[byteArray1[6] & 15];
					charArray1[13] = HEX_CHARS[byteArray1[6] >> 4];
					charArray1[14] = HEX_CHARS[byteArray1[7] & 15];
					charArray1[15] = HEX_CHARS[byteArray1[7] >> 4];
					charArray1[16] = HEX_CHARS[byteArray1[8] & 15];
					charArray1[17] = HEX_CHARS[byteArray1[8] >> 4];
					charArray1[18] = HEX_CHARS[byteArray1[9] & 15];
					charArray1[19] = HEX_CHARS[byteArray1[9] >> 4];
					charArray1[20] = HEX_CHARS[byteArray1[10] & 15];
					charArray1[21] = HEX_CHARS[byteArray1[10] >> 4];
					charArray1[22] = HEX_CHARS[byteArray1[11] & 15];
					charArray1[23] = HEX_CHARS[byteArray1[11] >> 4];
					charArray1[24] = HEX_CHARS[byteArray1[12] & 15];
					charArray1[25] = HEX_CHARS[byteArray1[12] >> 4];
					charArray1[26] = HEX_CHARS[byteArray1[13] & 15];
					charArray1[27] = HEX_CHARS[byteArray1[13] >> 4];
					charArray1[28] = HEX_CHARS[byteArray1[14] & 15];
					charArray1[29] = HEX_CHARS[byteArray1[14] >> 4];
					charArray1[30] = HEX_CHARS[byteArray1[15] & 15];
					charArray1[31] = HEX_CHARS[byteArray1[15] >> 4];
					_id = new string(charArray1);
				}
				Event ev = new Event(new DateTime(ticks), _level, _message, _data, _category, _code, _id);
				if (filter != null)
				{
					Filter f = filter;
					Monitor.Exit(monitor);
					try
					{
						if (!filter.Accepts(ev))
						{
							return;
						}
					}
					catch { }
					Monitor.Enter(monitor);
					if (isClosed)
					{
						return;
					}
				}
				try
				{
					Write(ev);
				}
				catch (Exception e)
				{
					exceptionHandler.Handle(new LoggerException(this, ev, e));
				}
				if (childCount > 0)
				{
					Monitor.Enter(childMonitor);
					//                    lock (childMonitor) {
					Monitor.Exit(monitor);
					for (int x = 0; x < childCount; x++)
					{
						children[x].LogEvent(_level, _message, _data, _category, _code, _id);
					}
					Monitor.Exit(childMonitor);
					//                    }
				}
				else
				{
					Monitor.Exit(monitor);
				}
			}
			//        }

		}

		/// <summary>
		/// Opens the logger so that it may receive calls to its Log() methods.  This results in a 
		/// call to the logger's virtual AcquireResources() method, as well as the starting of an 
		/// extra thread (in buffering mode only) and calls to the Open() method of any child loggers
		/// </summary>
		public void Open()
		{
			lock (monitor)
			{
				if (!isClosed)
				{
					return;
				}

				if (eventFormatter == null)
				{
					throw new ApplicationException("No event formatter set-- cannot start");
				}
				eventFormatter.Initialize();

				try
				{
					exceptionHandler.Open();
				}
				catch (Exception e)
				{
					throw new ApplicationException("Could not open exception handler", e);
				}
				int doubleBufferSize = bufferSize * 2;
				contextBuffer = new Context[doubleBufferSize];
				levelBuffer = new Level[doubleBufferSize];
				timestampBuffer = new long[doubleBufferSize];
				messageBuffer = new string[doubleBufferSize];
				dataBuffer = new object[doubleBufferSize];
				categoryBuffer = new string[doubleBufferSize];
				codeBuffer = new string[doubleBufferSize];
				idBuffer = new string[doubleBufferSize];

				contextWriteBuffer = new Context[doubleBufferSize];
				levelWriteBuffer = new Level[doubleBufferSize];
				timestampWriteBuffer = new long[doubleBufferSize];
				messageWriteBuffer = new string[doubleBufferSize];
				dataWriteBuffer = new object[doubleBufferSize];
				categoryWriteBuffer = new string[doubleBufferSize];
				codeWriteBuffer = new string[doubleBufferSize];
				idWriteBuffer = new string[doubleBufferSize];

				writeBuffer = new Event[doubleBufferSize];

				AcquireResources();

				if (isBufferingEnabled)
				{
					flushingThread = new FlushingThread(this, autoFlushInterval, threadPriority);
				}

				isClosed = false;

				lock (childMonitor)
				{
					for (int x = 0; x < childCount; x++)
					{
						children[x].Open();
					}
				}
			}
		}

		/// <summary>
		/// Closes the logger, releasing resources, closing all child loggers, and deregistering it as well
		/// </summary>
		public void Close()
		{
			lock (monitor)
			{
				if (isClosed)
				{
					return;
				}
				if (isBufferingEnabled)
				{
					flushingThread.Stop();
				}
				Flush();

				lock (writeBufferMonitor) { }

				lock (childMonitor)
				{
					for (int x = 0; x < childCount; x++)
					{
						try { children[x].Close(); }
						catch { }
					}
				}

				try
				{
					ReleaseResources();
				}
				catch { }

				if (isBufferingEnabled)
				{
					while (flushingThread.IsRunning())
					{
						try { Thread.Sleep(10); }
						catch { }
					}
				}

				try
				{
					exceptionHandler.Close();
				}
				catch { }

				try { Deregister(); }
				catch { }

				contextBuffer = null;
				levelBuffer = null;
				timestampBuffer = null;
				messageBuffer = null;
				dataBuffer = null;
				categoryBuffer = null;
				codeBuffer = null;
				idBuffer = null;

				contextWriteBuffer = null;
				levelWriteBuffer = null;
				timestampWriteBuffer = null;
				messageWriteBuffer = null;
				dataWriteBuffer = null;
				categoryWriteBuffer = null;
				codeWriteBuffer = null;
				idWriteBuffer = null;

				isClosed = true;
			}
		}



		/// <summary>
		/// Indicates the open/closed state of the logger
		/// </summary>
		public bool IsOpen
		{
			get
			{
				lock (monitor)
				{
					return (!isClosed);
				}
			}
		}

		/// <summary>
		/// Gets/sets the buffering ability of the logger.  This property cannot be set while the logger
		/// is open.
		/// </summary>
		public bool IsBufferingEnabled
		{
			get
			{
				lock (monitor)
				{
					return isBufferingEnabled;
				}
			}
			set
			{
				lock (monitor)
				{
					if (!isClosed)
					{
						throw new ApplicationException("Cannot set this property while the instance is running");
					}
					isBufferingEnabled = value;
				}
			}
		}

		/// <summary>
		/// Gets and sets whether the logger will immediately flush its buffer when logging
		/// an exception.  (This has no effect when the logger is not buffering-enabled)
		/// </summary>
		public bool IsExceptionAutoFlushEnabled
		{
			get
			{
				return isExceptionAutoFlushEnabled;
			}
			set
			{
				isExceptionAutoFlushEnabled = value;
			}
		}


		/// <summary>
		/// Gets and sets whether loggers, by default, will immediately flush their buffers when logging
		/// an exception.  (This has no effect on loggers which are not buffering-enabled)
		/// </summary>
		public static bool IsExceptionAutoFlushEnabledByDefault
		{
			get
			{
				return isExceptionAutoFlushEnabledByDefault;
			}
			set
			{
				isExceptionAutoFlushEnabledByDefault = value;
			}
		}

		/// <summary>
		/// Gets/sets the size of the buffer used by this instance.  
		/// Cannot be set while the logger is open
		/// </summary>
		public int BufferSize
		{
			get
			{
				return bufferSize;
			}
			set
			{
				lock (monitor)
				{
					if (IsOpen)
					{
						throw new ApplicationException("Cannot set this property while the logger is open");
					}
					bufferSize = Math.Max(value, MINIMUM_BUFFER_SIZE);
					maximumBufferSize = Math.Max(maximumBufferSize, bufferSize);
				}
			}
		}

		/// <summary>
		/// Gets/sets the absolute maximum size to which the buffer may grow.  
		/// The default is Int32.MaxValue
		/// </summary>
		public int MaximumBufferSize
		{
			get
			{
				return maximumBufferSize;
			}
			set
			{
				lock (monitor)
				{
					Flush();
					maximumBufferSize = Math.Max(value, bufferSize);
				}
			}
		}

		/// <summary>
		/// Gets the number of Event objects currently waiting in the buffer to be written to output (and child loggers)
		/// </summary>
		public int BufferCount
		{
			get
			{
				lock (monitor)
				{
					return bufferCount;
				}
			}
		}

		/// <summary>
		/// Gets/sets the automatic flushing interval that the logger uses when in buffering mode.
		/// This value (in milliseconds) is the delay between calls to the logger's Flush() method.
		/// (A logger that reaches its maximum capacity before the next scheduled automatic flushing
		/// may have its buffer flushed prematurely.)  If the new interval is less than the old,
		/// the buffer is immediately flushed.
		/// </summary>
		public int AutoFlushInterval
		{
			get
			{
				lock (monitor)
				{
					return autoFlushInterval;
				}
			}
			set
			{
				int newValue = Math.Max(value, MINIMUM_AUTO_FLUSH_INTERVAL);
				lock (monitor)
				{
					autoFlushInterval = newValue;
					if ((!isClosed) && (isBufferingEnabled))
					{
						int oldValue = flushingThread.FlushInterval;
						flushingThread.FlushInterval = newValue;
						if (oldValue > newValue)
						{
							Flush();
						}
					}
				}
			}
		}

		/// <summary>
		/// Gets/sets the priority of the internal thread used by this logger to flush its internal 
		/// buffer.  This property has no effect on a logger not in buffering mode, and cannot be set
		/// while the logger is open
		/// </summary>
		public ThreadPriority ThreadPriority
		{
			get
			{
				return threadPriority;
			}
			set
			{
				lock (monitor)
				{
					if (!isClosed)
					{
						throw new ApplicationException("Cannot set this property while the logger is open");
					}
					threadPriority = value;
				}
			}
		}

		/// <summary>
		/// Flushes the contents of the logger's buffer, writing the contents to output and to 
		/// the logger's children.  This method should not normally be called from application
		/// code, because this forces the application thread to drive
		/// the output process, losing the benefits of asynchronous operation; it is 
		/// provided for cases in which the application thread must
		/// ensure that events in the buffer are immediately written to output.  If
		/// IsExceptionAutoFlushEnabled is true and the logger is in buffering mode, the Flush() 
		/// method is called each time an exception is logged.
		/// </summary>
		public void Flush()
		{
			Monitor.Enter(monitor);
			//        lock (monitor) {
			if ((isClosed) || (bufferCount == 0))
			{
				Monitor.Exit(monitor);
				return;
			}
			Monitor.Enter(writeBufferMonitor);
			//            lock (writeBufferMonitor) {
			if (contextWriteBuffer == null)
			{
				int doubleBufferSize = bufferSize * 2;
				tempContextBuffer = new Context[doubleBufferSize];
				tempLevelBuffer = new Level[doubleBufferSize];
				tempTimestampBuffer = new long[doubleBufferSize];
				tempMessageBuffer = new string[doubleBufferSize];
				tempDataBuffer = new object[doubleBufferSize];
				tempCategoryBuffer = new string[doubleBufferSize];
				tempCodeBuffer = new string[doubleBufferSize];
				tempIDBuffer = new string[doubleBufferSize];
			}
			else
			{
				tempContextBuffer = contextWriteBuffer;
				tempLevelBuffer = levelWriteBuffer;
				tempTimestampBuffer = timestampWriteBuffer;
				tempMessageBuffer = messageWriteBuffer;
				tempDataBuffer = dataWriteBuffer;
				tempCategoryBuffer = categoryWriteBuffer;
				tempCodeBuffer = codeWriteBuffer;
				tempIDBuffer = idWriteBuffer;
			}

			contextWriteBuffer = contextBuffer;
			levelWriteBuffer = levelBuffer;
			timestampWriteBuffer = timestampBuffer;
			messageWriteBuffer = messageBuffer;
			dataWriteBuffer = dataBuffer;
			categoryWriteBuffer = categoryBuffer;
			codeWriteBuffer = codeBuffer;
			idWriteBuffer = idBuffer;
			writeBufferCount = bufferCount;

			contextBuffer = tempContextBuffer;
			levelBuffer = tempLevelBuffer;
			timestampBuffer = tempTimestampBuffer;
			messageBuffer = tempMessageBuffer;
			dataBuffer = tempDataBuffer;
			categoryBuffer = tempCategoryBuffer;
			codeBuffer = tempCodeBuffer;
			idBuffer = tempIDBuffer;

			// Array.Clear(buffer, 0, buffer.Length);
			bufferCount = 0;

			tempContextBuffer = null;
			tempLevelBuffer = null;
			tempTimestampBuffer = null;
			tempMessageBuffer = null;
			tempDataBuffer = null;
			tempCategoryBuffer = null;
			tempCodeBuffer = null;
			tempIDBuffer = null;

			Monitor.Exit(monitor);

			if ((writeBuffer == null) || (writeBuffer.Length < writeBufferCount))
			{
				writeBuffer = new Event[Math.Max(writeBufferCount, bufferSize)];
			}

			Event _event;
			string eventID;
			Context context;
			int writeBufferIndex;
			Filter f = filter;
			int owbc = writeBufferCount;

			// This can be expanded to squeeze out some more nanoseconds
			for (writeBufferIndex = 0; writeBufferIndex < writeBufferCount; writeBufferIndex++)
			{
				_event = new Event(
					new DateTime(timestampWriteBuffer[writeBufferIndex]),
					levelWriteBuffer[writeBufferIndex],
					messageWriteBuffer[writeBufferIndex],
					dataWriteBuffer[writeBufferIndex],
					categoryWriteBuffer[writeBufferIndex],
					codeWriteBuffer[writeBufferIndex]
				);

				context = contextWriteBuffer[writeBufferIndex];
				if (context != null)
				{
					_event.Context = context;
				}

				if (isIDGenerationEnabled)
				{
					eventID = idWriteBuffer[writeBufferIndex];
					if (eventID == null)
					{
						// This may look dorky, but using the System.Guid class
						// is three times as slow, and produces bad-looking hex strings to boot
						random2.NextBytes(byteArray2);
						charArray2[0] = HEX_CHARS[byteArray2[0] & 15];
						charArray2[1] = HEX_CHARS[byteArray2[0] >> 4];
						charArray2[2] = HEX_CHARS[byteArray2[1] & 15];
						charArray2[3] = HEX_CHARS[byteArray2[1] >> 4];
						charArray2[4] = HEX_CHARS[byteArray2[2] & 15];
						charArray2[5] = HEX_CHARS[byteArray2[2] >> 4];
						charArray2[6] = HEX_CHARS[byteArray2[3] & 15];
						charArray2[7] = HEX_CHARS[byteArray2[3] >> 4];
						charArray2[8] = HEX_CHARS[byteArray2[4] & 15];
						charArray2[9] = HEX_CHARS[byteArray2[4] >> 4];
						charArray2[10] = HEX_CHARS[byteArray2[5] & 15];
						charArray2[11] = HEX_CHARS[byteArray2[5] >> 4];
						charArray2[12] = HEX_CHARS[byteArray2[6] & 15];
						charArray2[13] = HEX_CHARS[byteArray2[6] >> 4];
						charArray2[14] = HEX_CHARS[byteArray2[7] & 15];
						charArray2[15] = HEX_CHARS[byteArray2[7] >> 4];
						charArray2[16] = HEX_CHARS[byteArray2[8] & 15];
						charArray2[17] = HEX_CHARS[byteArray2[8] >> 4];
						charArray2[18] = HEX_CHARS[byteArray2[9] & 15];
						charArray2[19] = HEX_CHARS[byteArray2[9] >> 4];
						charArray2[20] = HEX_CHARS[byteArray2[10] & 15];
						charArray2[21] = HEX_CHARS[byteArray2[10] >> 4];
						charArray2[22] = HEX_CHARS[byteArray2[11] & 15];
						charArray2[23] = HEX_CHARS[byteArray2[11] >> 4];
						charArray2[24] = HEX_CHARS[byteArray2[12] & 15];
						charArray2[25] = HEX_CHARS[byteArray2[12] >> 4];
						charArray2[26] = HEX_CHARS[byteArray2[13] & 15];
						charArray2[27] = HEX_CHARS[byteArray2[13] >> 4];
						charArray2[28] = HEX_CHARS[byteArray2[14] & 15];
						charArray2[29] = HEX_CHARS[byteArray2[14] >> 4];
						charArray2[30] = HEX_CHARS[byteArray2[15] & 15];
						charArray2[31] = HEX_CHARS[byteArray2[15] >> 4];
						eventID = new string(charArray2);
					}
					_event.ID = eventID;
				}

				if (f != null)
				{
					try
					{
						if (f.Accepts(_event))
						{
							writeBuffer[writeBufferIndex] = _event;
						}
						else
						{
							writeBufferIndex--;
							writeBufferCount--;
						}
					}
					catch
					{
						writeBuffer[writeBufferIndex] = _event;
					}
				}
				else
				{
					writeBuffer[writeBufferIndex] = _event;
				}

			}

			if (writeBufferCount > 0)
			{
				try
				{
					Write(writeBuffer, writeBufferCount);
				}
				catch (LoggerException le)
				{
					try
					{
						exceptionHandler.Handle(le);
					}
					catch { }
				}
			}

			if (contextWriteBuffer.Length >= Math.Min((bufferSize * 10), maximumBufferSize))
			{
				contextWriteBuffer = null;
				levelWriteBuffer = null;
				timestampWriteBuffer = null;
				messageWriteBuffer = null;
				dataWriteBuffer = null;
				categoryWriteBuffer = null;
				codeWriteBuffer = null;
				idWriteBuffer = null;
			}
			else
			{
				Array.Clear(contextWriteBuffer, 0, owbc);
				Array.Clear(levelWriteBuffer, 0, owbc);
				Array.Clear(timestampWriteBuffer, 0, owbc);
				Array.Clear(messageWriteBuffer, 0, owbc);
				Array.Clear(dataWriteBuffer, 0, owbc);
				Array.Clear(categoryWriteBuffer, 0, owbc);
				Array.Clear(codeWriteBuffer, 0, owbc);
				Array.Clear(idWriteBuffer, 0, owbc);
			}

			if ((childCount > 0) && (writeBufferCount > 0))
			{
				lock (childMonitor)
				{
					Event[] childWriteBuffer = new Event[writeBufferCount];
					Array.Copy(writeBuffer, 0, childWriteBuffer, 0, writeBufferCount);
					if (writeBuffer.Length > (bufferSize * 10))
					{
						writeBuffer = null;
					}
					else
					{
						Array.Clear(writeBuffer, 0, writeBufferCount);
					}
					writeBufferCount = 0;
					Monitor.Exit(writeBufferMonitor);
					for (int x = 0; x < childCount; x++)
					{
						children[x].Log(childWriteBuffer, childWriteBuffer.Length);
					}
				}
			}
			else
			{
				if (writeBuffer.Length > (bufferSize * 10))
				{
					writeBuffer = null;
				}
				else
				{
					Array.Clear(writeBuffer, 0, writeBufferCount);
				}
				writeBufferCount = 0;
				Monitor.Exit(writeBufferMonitor);
			}
			//            }
			//        }
		}

		private void Log(Event[] writeBuffer, int writeBufferCount)
		{
			Event[] events = new Event[writeBufferCount];
			Event e;
			int eventCount = 0;
			Filter f = filter;
			Monitor.Enter(monitor);
			//        lock (monitor) {
			if (isIDGenerationEnabled)
			{
				if (f == null)
				{
					for (int x = 0; x < writeBufferCount; x++)
					{
						e = writeBuffer[x];
						if (e.Level.Priority >= level.Priority)
						{
							if (e.ID == null)
							{
								// This may look dorky, but using the System.Guid class
								// is three times as slow, and produces bad-looking hex strings to boot
								random2.NextBytes(byteArray2);
								charArray2[0] = HEX_CHARS[byteArray2[0] & 15];
								charArray2[1] = HEX_CHARS[byteArray2[0] >> 4];
								charArray2[2] = HEX_CHARS[byteArray2[1] & 15];
								charArray2[3] = HEX_CHARS[byteArray2[1] >> 4];
								charArray2[4] = HEX_CHARS[byteArray2[2] & 15];
								charArray2[5] = HEX_CHARS[byteArray2[2] >> 4];
								charArray2[6] = HEX_CHARS[byteArray2[3] & 15];
								charArray2[7] = HEX_CHARS[byteArray2[3] >> 4];
								charArray2[8] = HEX_CHARS[byteArray2[4] & 15];
								charArray2[9] = HEX_CHARS[byteArray2[4] >> 4];
								charArray2[10] = HEX_CHARS[byteArray2[5] & 15];
								charArray2[11] = HEX_CHARS[byteArray2[5] >> 4];
								charArray2[12] = HEX_CHARS[byteArray2[6] & 15];
								charArray2[13] = HEX_CHARS[byteArray2[6] >> 4];
								charArray2[14] = HEX_CHARS[byteArray2[7] & 15];
								charArray2[15] = HEX_CHARS[byteArray2[7] >> 4];
								charArray2[16] = HEX_CHARS[byteArray2[8] & 15];
								charArray2[17] = HEX_CHARS[byteArray2[8] >> 4];
								charArray2[18] = HEX_CHARS[byteArray2[9] & 15];
								charArray2[19] = HEX_CHARS[byteArray2[9] >> 4];
								charArray2[20] = HEX_CHARS[byteArray2[10] & 15];
								charArray2[21] = HEX_CHARS[byteArray2[10] >> 4];
								charArray2[22] = HEX_CHARS[byteArray2[11] & 15];
								charArray2[23] = HEX_CHARS[byteArray2[11] >> 4];
								charArray2[24] = HEX_CHARS[byteArray2[12] & 15];
								charArray2[25] = HEX_CHARS[byteArray2[12] >> 4];
								charArray2[26] = HEX_CHARS[byteArray2[13] & 15];
								charArray2[27] = HEX_CHARS[byteArray2[13] >> 4];
								charArray2[28] = HEX_CHARS[byteArray2[14] & 15];
								charArray2[29] = HEX_CHARS[byteArray2[14] >> 4];
								charArray2[30] = HEX_CHARS[byteArray2[15] & 15];
								charArray2[31] = HEX_CHARS[byteArray2[15] >> 4];
								e.ID = new string(charArray2);
							}
							events[eventCount++] = e;
						}
					}
				}
				else
				{
					for (int x = 0; x < writeBufferCount; x++)
					{
						e = writeBuffer[x];
						if (e.Level.Priority >= level.Priority)
						{
							if (e.ID == null)
							{
								// This may look dorky, but using the System.Guid class
								// is three times as slow, and produces bad-looking hex strings to boot
								random2.NextBytes(byteArray2);
								charArray2[0] = HEX_CHARS[byteArray2[0] & 15];
								charArray2[1] = HEX_CHARS[byteArray2[0] >> 4];
								charArray2[2] = HEX_CHARS[byteArray2[1] & 15];
								charArray2[3] = HEX_CHARS[byteArray2[1] >> 4];
								charArray2[4] = HEX_CHARS[byteArray2[2] & 15];
								charArray2[5] = HEX_CHARS[byteArray2[2] >> 4];
								charArray2[6] = HEX_CHARS[byteArray2[3] & 15];
								charArray2[7] = HEX_CHARS[byteArray2[3] >> 4];
								charArray2[8] = HEX_CHARS[byteArray2[4] & 15];
								charArray2[9] = HEX_CHARS[byteArray2[4] >> 4];
								charArray2[10] = HEX_CHARS[byteArray2[5] & 15];
								charArray2[11] = HEX_CHARS[byteArray2[5] >> 4];
								charArray2[12] = HEX_CHARS[byteArray2[6] & 15];
								charArray2[13] = HEX_CHARS[byteArray2[6] >> 4];
								charArray2[14] = HEX_CHARS[byteArray2[7] & 15];
								charArray2[15] = HEX_CHARS[byteArray2[7] >> 4];
								charArray2[16] = HEX_CHARS[byteArray2[8] & 15];
								charArray2[17] = HEX_CHARS[byteArray2[8] >> 4];
								charArray2[18] = HEX_CHARS[byteArray2[9] & 15];
								charArray2[19] = HEX_CHARS[byteArray2[9] >> 4];
								charArray2[20] = HEX_CHARS[byteArray2[10] & 15];
								charArray2[21] = HEX_CHARS[byteArray2[10] >> 4];
								charArray2[22] = HEX_CHARS[byteArray2[11] & 15];
								charArray2[23] = HEX_CHARS[byteArray2[11] >> 4];
								charArray2[24] = HEX_CHARS[byteArray2[12] & 15];
								charArray2[25] = HEX_CHARS[byteArray2[12] >> 4];
								charArray2[26] = HEX_CHARS[byteArray2[13] & 15];
								charArray2[27] = HEX_CHARS[byteArray2[13] >> 4];
								charArray2[28] = HEX_CHARS[byteArray2[14] & 15];
								charArray2[29] = HEX_CHARS[byteArray2[14] >> 4];
								charArray2[30] = HEX_CHARS[byteArray2[15] & 15];
								charArray2[31] = HEX_CHARS[byteArray2[15] >> 4];
								e.ID = new string(charArray2);
							}
							try
							{
								if (f.Accepts(e))
								{
									events[eventCount++] = e;
								}
							}
							catch
							{
								events[eventCount++] = e;
							}
						}
					}
				}
			}
			else
			{
				if (f == null)
				{
					for (int x = 0; x < writeBufferCount; x++)
					{
						e = writeBuffer[x];
						if (e.Level.Priority >= level.Priority)
						{
							events[eventCount++] = e;
						}
					}
				}
				else
				{
					for (int x = 0; x < writeBufferCount; x++)
					{
						e = writeBuffer[x];
						if (e.Level.Priority >= level.Priority)
						{
							try
							{
								if (f.Accepts(e))
								{
									events[eventCount++] = e;
								}
							}
							catch
							{
								events[eventCount++] = e;
							}
						}
					}
				}
			}

			if (eventCount > 0)
			{
				try
				{
					Write(events, eventCount);
				}
				catch (LoggerException le)
				{
					exceptionHandler.Handle(le);
				}

				lock (childMonitor)
				{
					Monitor.Exit(monitor);
					for (int x = 0; x < childCount; x++)
					{
						children[x].Log(events, eventCount);
					}
				}
			}
			else
			{
				Monitor.Exit(monitor);
			}
			//        }
		}

		/// <summary>
		/// Subclasses should override this method to provide implementation-specific initialization behavior.
		/// Called by the Open() method
		/// </summary>
		protected virtual void AcquireResources()
		{

		}

		/// <summary>
		/// Subclasses should override this method to provide implementation-specific shutdown behavior.  
		/// Called by the Close() method
		/// </summary>
		protected virtual void ReleaseResources()
		{

		}

		/// <summary>
		/// Provides implementation-specific logic to write an Event to some output.  This is the
		/// only method required to be implemented by subclasses
		/// </summary>
		/// <param name="_event">The message to log</param>
		protected abstract void Write(Event _event);

		/// <summary>
		/// Provides the opportunity for subclasses to provide bulk-writing optimizations.
		/// The default behavior is just to loop through the array of Event objects, calling
		/// Write() once for each one, handling exceptions as necessary.  
		/// If this method is overridden, the subclass must
		/// explicitly construct one LoggerException for each exception encountered and
		/// pass it to the logger's ExceptionHandler.
		/// </summary>
		/// <param name="events">The array of events to write</param>
		/// <param name="length">The number of events to write from the array</param>
		protected virtual void Write(Event[] events, int length)
		{
			for (int x = 0; x < length; x++)
			{
				try
				{
					Write(events[x]);
				}
				catch (Exception e)
				{
					exceptionHandler.Handle(new LoggerException(this, events[x], e));
				}
			}
		}

		~Logger()
		{
			try { Close(); }
			catch { }
		}

		private class FlushingThread
		{
			private Thread thread;
			private Logger logger;
			private bool interrupted = false;
			private int flushInterval;
			private object monitor = new object();
			private bool running = true;

			public FlushingThread(Logger _logger, int _flushInterval, ThreadPriority _priority)
			{
				logger = _logger;
				flushInterval = _flushInterval;
				thread = new Thread(new ThreadStart(Run));
				thread.Priority = _priority;
				thread.Start();
			}

			public int FlushInterval
			{
				get
				{
					return flushInterval;
				}
				set
				{
					flushInterval = value;
				}
			}

			public void Stop()
			{
				lock (monitor)
				{
					if (!interrupted)
					{
						interrupted = true;
						Monitor.Pulse(monitor);
					}
				}
			}

			public bool IsRunning()
			{
				return running;
			}

			public void Awaken()
			{
				lock (monitor)
				{
					try
					{
						Monitor.Pulse(monitor);
					}
					catch { }
				}
			}

			void Run()
			{
				while (!interrupted)
				{
					try
					{
						lock (monitor)
						{
							Monitor.Wait(monitor, flushInterval);
						}
					}
					catch { }
					if (!interrupted)
					{
						try
						{
							logger.Flush();
						}
						catch { }
					}
				}
				running = false;
			}

			~FlushingThread()
			{
				Stop();
			}
		}

		/// <summary>
		/// A class that enables a logger to track its own registrations, in hopes of
		/// successfully deregistering itself later if need be
		/// </summary>
		private class Registration
		{
			public readonly string Name;
			public LoggerRegistry Registry;

			private Registration() { }

			public Registration(string name, LoggerRegistry registry)
			{
				Name = name;
				Registry = registry;
			}
		}


	}

}
